@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,118 @@
1
+ const { Client } = require("ldapts");
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
+ url: auth.url || cfg.url || auth.baseUrl || cfg.baseUrl || "ldap://localhost:389",
8
+ bindDN: auth.bindDN || cfg.bindDN || "",
9
+ bindPassword: auth.bindPassword || cfg.bindPassword || auth.password || cfg.password || "",
10
+ baseDN: auth.baseDN || cfg.baseDN || "",
11
+ };
12
+ }
13
+
14
+ function getToolDefinitions(connector) {
15
+ return [
16
+ {
17
+ type: "function",
18
+ function: {
19
+ name: `conn_${connector.id}_search`,
20
+ description: `Search the LDAP/Active Directory "${connector.name}" for users, groups, or objects.`,
21
+ parameters: {
22
+ type: "object",
23
+ properties: {
24
+ filter: { type: "string", description: "LDAP filter, e.g. (objectClass=person) or (mail=user@example.com)" },
25
+ base: { type: "string", description: "Search base DN (uses configured baseDN if omitted)" },
26
+ attributes: { type: "array", items: { type: "string" }, description: "Attributes to return (default: all)" },
27
+ },
28
+ required: ["filter"],
29
+ },
30
+ },
31
+ },
32
+ {
33
+ type: "function",
34
+ function: {
35
+ name: `conn_${connector.id}_get_user`,
36
+ description: `Look up a specific user in "${connector.name}" by username or email.`,
37
+ parameters: {
38
+ type: "object",
39
+ properties: {
40
+ username: { type: "string", description: "sAMAccountName, uid, or email" },
41
+ },
42
+ required: ["username"],
43
+ },
44
+ },
45
+ },
46
+ {
47
+ type: "function",
48
+ function: {
49
+ name: `conn_${connector.id}_list_groups`,
50
+ description: `List all groups in the LDAP directory "${connector.name}".`,
51
+ parameters: {
52
+ type: "object",
53
+ properties: {
54
+ base: { type: "string", description: "Base DN to search (optional)" },
55
+ },
56
+ required: [],
57
+ },
58
+ },
59
+ },
60
+ ];
61
+ }
62
+
63
+ function getAnthropicToolDefinitions(connector) {
64
+ return getToolDefinitions(connector).map(t => ({
65
+ name: t.function.name,
66
+ description: t.function.description,
67
+ input_schema: t.function.parameters,
68
+ }));
69
+ }
70
+
71
+ async function executeTool(action, args, connector) {
72
+ const config = getConfig(connector);
73
+ if (!config.url) return "LDAP URL not configured.";
74
+
75
+ const client = new Client({ url: config.url, tlsOptions: { rejectUnauthorized: false } });
76
+
77
+ try {
78
+ if (config.bindDN) await client.bind(config.bindDN, config.bindPassword);
79
+
80
+ if (action === "search") {
81
+ const { searchEntries } = await client.search(args.base || config.baseDN, {
82
+ filter: args.filter,
83
+ attributes: args.attributes || ["*"],
84
+ scope: "sub",
85
+ sizeLimit: 100,
86
+ });
87
+ return JSON.stringify(searchEntries, null, 2).slice(0, 8000);
88
+ }
89
+
90
+ if (action === "get_user") {
91
+ const filter = args.username.includes("@")
92
+ ? `(mail=${args.username})`
93
+ : `(|(sAMAccountName=${args.username})(uid=${args.username}))`;
94
+ const { searchEntries } = await client.search(config.baseDN, { filter, scope: "sub", sizeLimit: 5 });
95
+ return searchEntries.length
96
+ ? JSON.stringify(searchEntries[0], null, 2).slice(0, 4000)
97
+ : "User not found.";
98
+ }
99
+
100
+ if (action === "list_groups") {
101
+ const { searchEntries } = await client.search(args.base || config.baseDN, {
102
+ filter: "(objectClass=group)",
103
+ attributes: ["cn", "description", "member"],
104
+ scope: "sub",
105
+ sizeLimit: 200,
106
+ });
107
+ return searchEntries.map(g => g.cn).join("\n") || "No groups found.";
108
+ }
109
+
110
+ return `Unknown LDAP action: ${action}`;
111
+ } catch (err) {
112
+ return `LDAP error: ${err.message}`;
113
+ } finally {
114
+ await client.unbind().catch(() => {});
115
+ }
116
+ }
117
+
118
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,138 @@
1
+ const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
2
+ const { StdioClientTransport } = require("@modelcontextprotocol/sdk/client/stdio.js");
3
+ const { SSEClientTransport } = require("@modelcontextprotocol/sdk/client/sse.js");
4
+ const axios = require("axios");
5
+
6
+ function getConfig(connector) {
7
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
8
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
9
+ return {
10
+ sseUrl: auth.baseUrl || cfg.baseUrl || auth.sseUrl || cfg.sseUrl || "",
11
+ command: auth.command || cfg.command || "",
12
+ args: auth.args || cfg.args || [],
13
+ bearerToken: auth.bearerToken || cfg.bearerToken || "",
14
+ };
15
+ }
16
+
17
+ async function withMcpClient(config, fn) {
18
+ const client = new Client({ name: "open-enthrium", version: "1.0.0" }, { capabilities: {} });
19
+
20
+ let transport;
21
+ if (config.sseUrl) {
22
+ const headers = config.bearerToken ? { Authorization: `Bearer ${config.bearerToken}` } : {};
23
+ transport = new SSEClientTransport(new URL(config.sseUrl), { requestInit: { headers } });
24
+ } else if (config.command) {
25
+ transport = new StdioClientTransport({ command: config.command, args: config.args });
26
+ } else {
27
+ throw new Error("MCP connector requires either sseUrl or command.");
28
+ }
29
+
30
+ await client.connect(transport);
31
+ try {
32
+ return await fn(client);
33
+ } finally {
34
+ await client.close().catch(() => {});
35
+ }
36
+ }
37
+
38
+ function getToolDefinitions(connector) {
39
+ return [
40
+ {
41
+ type: "function",
42
+ function: {
43
+ name: `conn_${connector.id}_list_tools`,
44
+ description: `List all tools available on the MCP server "${connector.name}".`,
45
+ parameters: { type: "object", properties: {}, required: [] },
46
+ },
47
+ },
48
+ {
49
+ type: "function",
50
+ function: {
51
+ name: `conn_${connector.id}_call_tool`,
52
+ description: `Call a specific tool on the MCP server "${connector.name}".`,
53
+ parameters: {
54
+ type: "object",
55
+ properties: {
56
+ tool: { type: "string", description: "Tool name to call" },
57
+ arguments: { type: "object", description: "Arguments to pass to the tool", additionalProperties: true },
58
+ },
59
+ required: ["tool"],
60
+ },
61
+ },
62
+ },
63
+ {
64
+ type: "function",
65
+ function: {
66
+ name: `conn_${connector.id}_list_resources`,
67
+ description: `List resources exposed by the MCP server "${connector.name}".`,
68
+ parameters: { type: "object", properties: {}, required: [] },
69
+ },
70
+ },
71
+ {
72
+ type: "function",
73
+ function: {
74
+ name: `conn_${connector.id}_read_resource`,
75
+ description: `Read a resource from the MCP server "${connector.name}".`,
76
+ parameters: {
77
+ type: "object",
78
+ properties: {
79
+ uri: { type: "string", description: "Resource URI" },
80
+ },
81
+ required: ["uri"],
82
+ },
83
+ },
84
+ },
85
+ ];
86
+ }
87
+
88
+ function getAnthropicToolDefinitions(connector) {
89
+ return getToolDefinitions(connector).map(t => ({
90
+ name: t.function.name,
91
+ description: t.function.description,
92
+ input_schema: t.function.parameters,
93
+ }));
94
+ }
95
+
96
+ async function executeTool(action, args, connector) {
97
+ const config = getConfig(connector);
98
+
99
+ try {
100
+ if (action === "list_tools") {
101
+ return await withMcpClient(config, async client => {
102
+ const { tools } = await client.listTools();
103
+ return tools.map(t => `${t.name}: ${t.description || ""}`).join("\n") || "No tools found.";
104
+ });
105
+ }
106
+
107
+ if (action === "call_tool") {
108
+ if (!args.tool) return "Tool name required.";
109
+ return await withMcpClient(config, async client => {
110
+ const result = await client.callTool({ name: args.tool, arguments: args.arguments || {} });
111
+ const content = result.content || [];
112
+ return content.map(c => c.text || JSON.stringify(c)).join("\n").slice(0, 8000);
113
+ });
114
+ }
115
+
116
+ if (action === "list_resources") {
117
+ return await withMcpClient(config, async client => {
118
+ const { resources } = await client.listResources();
119
+ return resources.map(r => `${r.uri} — ${r.name || ""}`).join("\n") || "No resources found.";
120
+ });
121
+ }
122
+
123
+ if (action === "read_resource") {
124
+ if (!args.uri) return "Resource URI required.";
125
+ return await withMcpClient(config, async client => {
126
+ const result = await client.readResource({ uri: args.uri });
127
+ const content = result.contents || [];
128
+ return content.map(c => c.text || JSON.stringify(c)).join("\n").slice(0, 8000);
129
+ });
130
+ }
131
+
132
+ return `Unknown MCP action: ${action}`;
133
+ } catch (err) {
134
+ return `MCP error: ${err.message}`;
135
+ }
136
+ }
137
+
138
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,119 @@
1
+ const { MongoClient } = require("mongodb");
2
+
3
+ function getToolDefinitions(connector) {
4
+ return [
5
+ {
6
+ type: "function",
7
+ function: {
8
+ name: `conn_${connector.id}_find`,
9
+ description: `Search documents in the "${connector.name}" MongoDB database. Use for filtering, retrieving, and looking up records.`,
10
+ parameters: {
11
+ type: "object",
12
+ properties: {
13
+ collection: { type: "string", description: "Collection name to query." },
14
+ filter: { type: "object", description: "MongoDB filter (e.g. {\"status\": \"active\"}). Use {} for all." },
15
+ projection: { type: "object", description: "Fields to include/exclude (optional)." },
16
+ limit: { type: "number", description: "Max results to return (default 20, max 100)." },
17
+ },
18
+ required: ["collection", "filter"]
19
+ }
20
+ }
21
+ },
22
+ {
23
+ type: "function",
24
+ function: {
25
+ name: `conn_${connector.id}_aggregate`,
26
+ description: `Run a MongoDB aggregation pipeline on the "${connector.name}" database. Use for grouping, counting, summing, or analytics.`,
27
+ parameters: {
28
+ type: "object",
29
+ properties: {
30
+ collection: { type: "string", description: "Collection name." },
31
+ pipeline: { type: "array", description: "Aggregation pipeline stages (e.g. [{\"$group\": {\"_id\": \"$status\", \"count\": {\"$sum\": 1}}}])." },
32
+ },
33
+ required: ["collection", "pipeline"]
34
+ }
35
+ }
36
+ }
37
+ ];
38
+ }
39
+
40
+ function getAnthropicToolDefinitions(connector) {
41
+ return [
42
+ {
43
+ name: `conn_${connector.id}_find`,
44
+ description: `Search documents in the "${connector.name}" MongoDB database.`,
45
+ input_schema: {
46
+ type: "object",
47
+ properties: {
48
+ collection: { type: "string", description: "Collection name to query." },
49
+ filter: { type: "object", description: "MongoDB filter document." },
50
+ projection: { type: "object", description: "Fields to include/exclude (optional)." },
51
+ limit: { type: "number", description: "Max results (default 20, max 100)." },
52
+ },
53
+ required: ["collection", "filter"]
54
+ }
55
+ },
56
+ {
57
+ name: `conn_${connector.id}_aggregate`,
58
+ description: `Run a MongoDB aggregation pipeline on the "${connector.name}" database.`,
59
+ input_schema: {
60
+ type: "object",
61
+ properties: {
62
+ collection: { type: "string", description: "Collection name." },
63
+ pipeline: { type: "array", description: "Aggregation pipeline stages." },
64
+ },
65
+ required: ["collection", "pipeline"]
66
+ }
67
+ }
68
+ ];
69
+ }
70
+
71
+ function buildUri(cfg, auth) {
72
+ if (cfg.uri) return cfg.uri;
73
+ const creds = auth.username ? `${encodeURIComponent(auth.username)}:${encodeURIComponent(auth.password || "")}@` : "";
74
+ const host = cfg.host || "localhost";
75
+ const port = cfg.port || "27017";
76
+ const db = cfg.database ? `/${cfg.database}` : "";
77
+ return `mongodb://${creds}${host}:${port}${db}`;
78
+ }
79
+
80
+ async function executeTool(action, args, connector) {
81
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
82
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
83
+ const uri = buildUri(cfg, auth);
84
+
85
+ const client = new MongoClient(uri, { serverSelectionTimeoutMS: 10000, connectTimeoutMS: 10000 });
86
+ try {
87
+ await client.connect();
88
+ const db = client.db(cfg.database || undefined);
89
+
90
+ if (action === "find") {
91
+ const { collection, filter = {}, projection, limit = 20 } = args;
92
+ if (!collection) return "Collection name required.";
93
+ const safeLimit = Math.min(parseInt(limit) || 20, 100);
94
+ const opts = projection ? { projection } : {};
95
+ const docs = await db.collection(collection).find(filter, opts).limit(safeLimit).toArray();
96
+ if (!docs.length) return "No documents found.";
97
+ return JSON.stringify(docs, null, 2);
98
+ }
99
+
100
+ if (action === "aggregate") {
101
+ const { collection, pipeline = [] } = args;
102
+ if (!collection) return "Collection name required.";
103
+ if (!Array.isArray(pipeline)) return "Pipeline must be an array.";
104
+ const hasLimit = pipeline.some(s => "$limit" in s);
105
+ const safePipeline = hasLimit ? pipeline : [...pipeline, { $limit: 100 }];
106
+ const docs = await db.collection(collection).aggregate(safePipeline).toArray();
107
+ if (!docs.length) return "Aggregation returned no results.";
108
+ return JSON.stringify(docs, null, 2);
109
+ }
110
+
111
+ return `Unknown MongoDB action: ${action}`;
112
+ } catch (err) {
113
+ return `MongoDB error: ${err.message}`;
114
+ } finally {
115
+ await client.close().catch(() => {});
116
+ }
117
+ }
118
+
119
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool, buildUri };
@@ -0,0 +1,106 @@
1
+ const mqtt = require("mqtt");
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
+ brokerUrl: auth.brokerUrl || cfg.brokerUrl || auth.baseUrl || cfg.baseUrl || "mqtt://localhost:1883",
8
+ username: auth.username || cfg.username || undefined,
9
+ password: auth.password || cfg.password || undefined,
10
+ clientId: auth.clientId || cfg.clientId || `oe-${Date.now()}`,
11
+ };
12
+ }
13
+
14
+ function connect(config) {
15
+ return new Promise((resolve, reject) => {
16
+ const client = mqtt.connect(config.brokerUrl, {
17
+ clientId: config.clientId,
18
+ username: config.username,
19
+ password: config.password,
20
+ connectTimeout: 10000,
21
+ });
22
+ client.on("connect", () => resolve(client));
23
+ client.on("error", reject);
24
+ });
25
+ }
26
+
27
+ function getToolDefinitions(connector) {
28
+ return [
29
+ {
30
+ type: "function",
31
+ function: {
32
+ name: `conn_${connector.id}_publish`,
33
+ description: `Publish a message to an MQTT topic on "${connector.name}".`,
34
+ parameters: {
35
+ type: "object",
36
+ properties: {
37
+ topic: { type: "string", description: "MQTT topic" },
38
+ message: { type: "string", description: "Message payload" },
39
+ qos: { type: "number", description: "QoS level (0, 1, or 2). Default 0." },
40
+ },
41
+ required: ["topic", "message"],
42
+ },
43
+ },
44
+ },
45
+ {
46
+ type: "function",
47
+ function: {
48
+ name: `conn_${connector.id}_subscribe`,
49
+ description: `Subscribe to an MQTT topic and read recent messages from "${connector.name}".`,
50
+ parameters: {
51
+ type: "object",
52
+ properties: {
53
+ topic: { type: "string", description: "MQTT topic or wildcard (e.g. sensors/#)" },
54
+ timeout: { type: "number", description: "Seconds to listen (default 5, max 30)" },
55
+ },
56
+ required: ["topic"],
57
+ },
58
+ },
59
+ },
60
+ ];
61
+ }
62
+
63
+ function getAnthropicToolDefinitions(connector) {
64
+ return getToolDefinitions(connector).map(t => ({
65
+ name: t.function.name,
66
+ description: t.function.description,
67
+ input_schema: t.function.parameters,
68
+ }));
69
+ }
70
+
71
+ async function executeTool(action, args, connector) {
72
+ const config = getConfig(connector);
73
+
74
+ try {
75
+ if (action === "publish") {
76
+ const client = await connect(config);
77
+ await new Promise((res, rej) =>
78
+ client.publish(args.topic, args.message, { qos: args.qos || 0 }, err => err ? rej(err) : res())
79
+ );
80
+ client.end();
81
+ return `Published to "${args.topic}".`;
82
+ }
83
+
84
+ if (action === "subscribe") {
85
+ const client = await connect(config);
86
+ const timeout = Math.min(parseInt(args.timeout) || 5, 30) * 1000;
87
+ const messages = [];
88
+
89
+ await new Promise((resolve) => {
90
+ client.subscribe(args.topic, { qos: 1 });
91
+ client.on("message", (topic, payload) => {
92
+ messages.push(`[${topic}] ${payload.toString()}`);
93
+ });
94
+ setTimeout(resolve, timeout);
95
+ });
96
+ client.end();
97
+ return messages.length ? messages.join("\n") : "No messages received within timeout.";
98
+ }
99
+
100
+ return `Unknown MQTT action: ${action}`;
101
+ } catch (err) {
102
+ return `MQTT error: ${err.message}`;
103
+ }
104
+ }
105
+
106
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,118 @@
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}_generate_music`,
10
+ description: `Generate music or audio from a text prompt using "${connector.name}". Returns audio URLs (generation may be async).`,
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ prompt: { type: "string", description: "Description of the music to generate (genre, mood, instruments, tempo, etc.)" },
15
+ duration: { type: "number", description: "Duration in seconds (default 30, max 240)" },
16
+ title: { type: "string", description: "Optional song title" },
17
+ instrumental:{ type: "boolean", description: "If true, generate instrumental music without vocals" },
18
+ },
19
+ required: ["prompt"],
20
+ },
21
+ },
22
+ },
23
+ {
24
+ type: "function",
25
+ function: {
26
+ name: `conn_${connector.id}_check_status`,
27
+ description: `Check the status of a music generation job. Pass the job_id returned by generate_music.`,
28
+ parameters: {
29
+ type: "object",
30
+ properties: {
31
+ job_id: { type: "string", description: "Job ID returned by generate_music" },
32
+ },
33
+ required: ["job_id"],
34
+ },
35
+ },
36
+ },
37
+ ];
38
+ }
39
+
40
+ function getAnthropicToolDefinitions(connector) {
41
+ return getToolDefinitions(connector).map(t => ({
42
+ name: t.function.name,
43
+ description: t.function.description,
44
+ input_schema: t.function.parameters,
45
+ }));
46
+ }
47
+
48
+ async function executeTool(action, args, connector) {
49
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
50
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
51
+ const creds = { ...cfg, ...auth };
52
+ const type = connector.type;
53
+
54
+ // ── Suno ──────────────────────────────────────────────────────────────────
55
+ if (type === "suno") {
56
+ if (action === "generate_music") {
57
+ const resp = await axios.post(
58
+ "https://studio-api.suno.ai/api/external/generate/",
59
+ {
60
+ prompt: args.prompt,
61
+ tags: args.prompt,
62
+ title: args.title || "Generated Track",
63
+ make_instrumental: args.instrumental || false,
64
+ wait_audio: false,
65
+ },
66
+ { headers: { Authorization: `Bearer ${creds.apiKey}`, "Content-Type": "application/json" } }
67
+ );
68
+ const clips = resp.data || [];
69
+ if (Array.isArray(clips) && clips.length) {
70
+ const ids = clips.map(c => c.id).join(", ");
71
+ return `Music generation started. Clip IDs: ${ids}. Use check_status with any ID to get the audio URL.`;
72
+ }
73
+ return `Music generation started. Response: ${JSON.stringify(resp.data).slice(0, 200)}`;
74
+ }
75
+ if (action === "check_status") {
76
+ const resp = await axios.get(
77
+ `https://studio-api.suno.ai/api/external/clips/?ids=${args.job_id}`,
78
+ { headers: { Authorization: `Bearer ${creds.apiKey}` } }
79
+ );
80
+ const clip = (resp.data?.clips || resp.data)?.[0];
81
+ if (!clip) return "Clip not found.";
82
+ if (clip.status === "complete") return `Music ready:\n Title: ${clip.title}\n URL: ${clip.audio_url}\n Duration: ${clip.duration}s`;
83
+ return `Status: ${clip.status}. Check again in a few seconds.`;
84
+ }
85
+ }
86
+
87
+ // ── Udio ──────────────────────────────────────────────────────────────────
88
+ if (type === "udio") {
89
+ if (action === "generate_music") {
90
+ const resp = await axios.post(
91
+ "https://www.udio.com/api/generate-proxy",
92
+ {
93
+ prompt: args.prompt,
94
+ samplerOptions: { seed: -1 },
95
+ },
96
+ { headers: { Authorization: `Bearer ${creds.apiKey}`, "Content-Type": "application/json" } }
97
+ );
98
+ const trackIds = (resp.data?.track_ids || []).join(", ");
99
+ return trackIds
100
+ ? `Music generation started. Track IDs: ${trackIds}. Use check_status to get the audio URL.`
101
+ : `Music generation started. Response: ${JSON.stringify(resp.data).slice(0, 200)}`;
102
+ }
103
+ if (action === "check_status") {
104
+ const resp = await axios.get(
105
+ `https://www.udio.com/api/songs?songIds=${args.job_id}`,
106
+ { headers: { Authorization: `Bearer ${creds.apiKey}` } }
107
+ );
108
+ const song = (resp.data?.songs || [])[0];
109
+ if (!song) return "Song not found.";
110
+ if (song.finished) return `Music ready:\n Title: ${song.title || "Track"}\n URL: ${song.song_path}`;
111
+ return `Status: generating. Check again in a few seconds.`;
112
+ }
113
+ }
114
+
115
+ return `Unsupported action "${action}" for music connector type: ${type}`;
116
+ }
117
+
118
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,93 @@
1
+ const axios = require("axios");
2
+
3
+ function cfg(connector) {
4
+ return connector.authConfig ? JSON.parse(connector.authConfig) : {};
5
+ }
6
+
7
+ function client({ integrationToken }) {
8
+ return axios.create({
9
+ baseURL: "https://api.notion.com/v1",
10
+ headers: { Authorization: `Bearer ${integrationToken}`, "Notion-Version": "2022-06-28", "Content-Type": "application/json" },
11
+ });
12
+ }
13
+
14
+ const TOOLS = c => [
15
+ { action: "search", desc: `Search Notion pages and databases via ${c.name}.`,
16
+ params: { query: { type: "string", description: "Search query." } }, required: ["query"] },
17
+ { action: "get_page", desc: `Get content of a Notion page via ${c.name}.`,
18
+ params: { pageId: { type: "string", description: "Notion page ID." } }, required: ["pageId"] },
19
+ { action: "create_page", desc: `Create a new Notion page via ${c.name}.`,
20
+ params: { parentId: { type: "string", description: "Parent page ID to create the page under." },
21
+ title: { type: "string", description: "Page title." },
22
+ content: { type: "string", description: "Page content (plain text)." } }, required: ["parentId","title"] },
23
+ ];
24
+
25
+ function getToolDefinitions(connector) {
26
+ return TOOLS(connector).map(t => ({
27
+ type: "function",
28
+ function: { name: `conn_${connector.id}_${t.action}`, description: t.desc,
29
+ parameters: { type: "object", properties: t.params, required: t.required } },
30
+ }));
31
+ }
32
+
33
+ function getAnthropicToolDefinitions(connector) {
34
+ return TOOLS(connector).map(t => ({
35
+ name: `conn_${connector.id}_${t.action}`, description: t.desc,
36
+ input_schema: { type: "object", properties: t.params, required: t.required },
37
+ }));
38
+ }
39
+
40
+ async function executeTool(action, args, connector) {
41
+ const creds = cfg(connector);
42
+ if (!creds.integrationToken) return "Notion not configured. Please add credentials in Integrations.";
43
+ const api = client(creds);
44
+
45
+ try {
46
+ if (action === "search") {
47
+ const res = await api.post("/search", { query: args.query, page_size: 10 });
48
+ const results = res.data.results || [];
49
+ if (!results.length) return "No results found.";
50
+ return results.map(r => {
51
+ const title = r.properties?.title?.title?.[0]?.plain_text || r.properties?.Name?.title?.[0]?.plain_text || "(untitled)";
52
+ return `[${r.id}] ${title} (${r.object})`;
53
+ }).join("\n");
54
+ }
55
+
56
+ if (action === "get_page") {
57
+ const [page, blocks] = await Promise.all([
58
+ api.get(`/pages/${args.pageId}`),
59
+ api.get(`/blocks/${args.pageId}/children?page_size=50`),
60
+ ]);
61
+ const title = Object.values(page.data.properties || {}).find(p => p.type === "title")?.title?.[0]?.plain_text || "(untitled)";
62
+ const content = (blocks.data.results || []).map(b => {
63
+ const type = b.type;
64
+ return b[type]?.rich_text?.map(t => t.plain_text).join("") || "";
65
+ }).filter(Boolean).join("\n");
66
+ return `Title: ${title}\n\n${content.slice(0, 3000)}`;
67
+ }
68
+
69
+ if (action === "create_page") {
70
+ const { parentId, title, content = "" } = args;
71
+ const res = await api.post("/pages", {
72
+ parent: { page_id: parentId },
73
+ properties: { title: { title: [{ type: "text", text: { content: title } }] } },
74
+ children: content ? [{ object: "block", type: "paragraph",
75
+ paragraph: { rich_text: [{ type: "text", text: { content: content.slice(0, 2000) } }] } }] : [],
76
+ });
77
+ return `Created Notion page: ${title} (ID: ${res.data.id})`;
78
+ }
79
+
80
+ return `Unknown Notion action: ${action}`;
81
+ } catch (err) {
82
+ return `Notion error: ${err.response?.data?.message || err.message}`;
83
+ }
84
+ }
85
+
86
+ async function testConnection(authConfig) {
87
+ try {
88
+ const res = await client(authConfig).post("/search", { page_size: 1 });
89
+ return Array.isArray(res.data.results);
90
+ } catch { return false; }
91
+ }
92
+
93
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool, testConnection };