@vibetools/dokploy-mcp 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -16,32 +16,57 @@ function deepFreeze(value) {
|
|
|
16
16
|
}
|
|
17
17
|
return value;
|
|
18
18
|
}
|
|
19
|
+
const ARROW_FN_RE = /^\s*async\s*\(/;
|
|
20
|
+
/**
|
|
21
|
+
* Auto-wraps raw code into an async function if the agent didn't provide one.
|
|
22
|
+
* Accepts all these forms:
|
|
23
|
+
* 1. async ({ dokploy }) => { ... } -- arrow with destructuring (original)
|
|
24
|
+
* 2. async (ctx) => { ... } -- arrow with param
|
|
25
|
+
* 3. async () => dokploy.project.all() -- arrow using globals
|
|
26
|
+
* 4. dokploy.project.all() -- raw expression (auto-wrapped)
|
|
27
|
+
* 5. const x = await dokploy.project.all() -- raw statements (auto-wrapped)
|
|
28
|
+
*/
|
|
29
|
+
function wrapSandboxCode(code) {
|
|
30
|
+
const trimmed = code.trim();
|
|
31
|
+
if (ARROW_FN_RE.test(trimmed)) {
|
|
32
|
+
return trimmed;
|
|
33
|
+
}
|
|
34
|
+
// Raw code -- wrap in an async function that receives context but ignores it
|
|
35
|
+
// (context keys are already available as globals)
|
|
36
|
+
const hasReturn = /\breturn\b/.test(trimmed);
|
|
37
|
+
const body = hasReturn ? trimmed : `return (${trimmed})`;
|
|
38
|
+
return `async () => { ${body} }`;
|
|
39
|
+
}
|
|
19
40
|
export async function runSandboxedFunction({ code, context, limits: providedLimits, }) {
|
|
20
41
|
const limits = providedLimits ?? resolveSandboxLimits();
|
|
21
42
|
const logs = [];
|
|
22
43
|
let loggedBytes = 0;
|
|
23
44
|
const frozenContext = deepFreeze(context);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
catch {
|
|
34
|
-
return String(arg);
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
.join(' ');
|
|
38
|
-
loggedBytes += Buffer.byteLength(line, 'utf8');
|
|
39
|
-
if (loggedBytes > limits.maxLogBytes) {
|
|
40
|
-
throw new Error(`Sandbox logs exceeded ${limits.maxLogBytes} bytes.`);
|
|
45
|
+
const consoleProxy = {
|
|
46
|
+
log: (...args) => {
|
|
47
|
+
const line = args
|
|
48
|
+
.map((arg) => {
|
|
49
|
+
try {
|
|
50
|
+
return typeof arg === 'string' ? arg : JSON.stringify(arg);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return String(arg);
|
|
41
54
|
}
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
})
|
|
56
|
+
.join(' ');
|
|
57
|
+
loggedBytes += Buffer.byteLength(line, 'utf8');
|
|
58
|
+
if (loggedBytes > limits.maxLogBytes) {
|
|
59
|
+
throw new Error(`Sandbox logs exceeded ${limits.maxLogBytes} bytes.`);
|
|
60
|
+
}
|
|
61
|
+
logs.push(line);
|
|
44
62
|
},
|
|
63
|
+
};
|
|
64
|
+
const sandbox = createContext({
|
|
65
|
+
__context: frozenContext,
|
|
66
|
+
// Expose context keys as top-level globals so agents can write
|
|
67
|
+
// `dokploy.project.all()` or `catalog.searchText("x")` directly
|
|
68
|
+
...frozenContext,
|
|
69
|
+
console: consoleProxy,
|
|
45
70
|
process: undefined,
|
|
46
71
|
fetch: undefined,
|
|
47
72
|
require: undefined,
|
|
@@ -63,15 +88,13 @@ export async function runSandboxedFunction({ code, context, limits: providedLimi
|
|
|
63
88
|
wasm: false,
|
|
64
89
|
},
|
|
65
90
|
});
|
|
91
|
+
const wrappedCode = wrapSandboxCode(code);
|
|
66
92
|
const script = new Script(`
|
|
67
93
|
(async () => {
|
|
68
|
-
const __fn = (${
|
|
94
|
+
const __fn = (${wrappedCode})
|
|
69
95
|
if (typeof __fn !== 'function') {
|
|
70
96
|
throw new Error('Sandbox code must evaluate to a function.')
|
|
71
97
|
}
|
|
72
|
-
if (__fn.constructor?.name !== 'AsyncFunction') {
|
|
73
|
-
throw new Error('Sandbox code must evaluate to an async function.')
|
|
74
|
-
}
|
|
75
98
|
return await __fn(__context)
|
|
76
99
|
})()
|
|
77
100
|
`, {
|
|
@@ -11,8 +11,12 @@ const executeSchema = z
|
|
|
11
11
|
code: z
|
|
12
12
|
.string()
|
|
13
13
|
.min(1)
|
|
14
|
-
.describe('
|
|
15
|
-
'
|
|
14
|
+
.describe('JavaScript code to run. Can be a simple expression, statements, or an async function. ' +
|
|
15
|
+
'dokploy and helpers are available as globals. ' +
|
|
16
|
+
'Examples: ' +
|
|
17
|
+
'`await dokploy.project.all()` | ' +
|
|
18
|
+
'`const app = await dokploy.application.one({ applicationId: "id" }); return app.name` | ' +
|
|
19
|
+
'`async ({ dokploy }) => dokploy.settings.health()`. ' +
|
|
16
20
|
'dokploy.<module>.<method>(params) calls the Dokploy API. ' +
|
|
17
21
|
'helpers: sleep(ms), assert(cond, msg), pick(obj, keys), limit(arr, n), selectOne(arr, pred).'),
|
|
18
22
|
})
|
|
@@ -48,13 +52,13 @@ export async function runExecuteWithHost(code, host) {
|
|
|
48
52
|
export const executeTool = createTool({
|
|
49
53
|
name: 'execute',
|
|
50
54
|
title: 'Execute Dokploy Workflow',
|
|
51
|
-
description: '
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
55
|
+
description: 'Run JavaScript code against the Dokploy API. ' +
|
|
56
|
+
'`dokploy` and `helpers` are available as globals -- no wrapper function needed. ' +
|
|
57
|
+
'Just write: `await dokploy.project.all()` or multi-line with `const`/`return`. ' +
|
|
58
|
+
'dokploy.<module>.<method>(params) calls the API. ' +
|
|
59
|
+
'Modules: project, environment, application, compose, domain, postgres, mysql, mariadb, mongo, redis, ' +
|
|
56
60
|
'deployment, docker, server, settings, user, notification, backup, mounts, registry, certificates, and more. ' +
|
|
57
|
-
'Use search tool first to discover
|
|
61
|
+
'Use search tool first to discover procedure names and required parameters.',
|
|
58
62
|
schema: executeSchema,
|
|
59
63
|
annotations: { openWorldHint: true },
|
|
60
64
|
handler: async ({ input }) => {
|
|
@@ -9,10 +9,10 @@ const searchSchema = z
|
|
|
9
9
|
code: z
|
|
10
10
|
.string()
|
|
11
11
|
.min(1)
|
|
12
|
-
.describe('
|
|
13
|
-
'
|
|
14
|
-
'catalog
|
|
15
|
-
'
|
|
12
|
+
.describe('JavaScript code to search the API catalog. `catalog` is available as a global. ' +
|
|
13
|
+
'Examples: `catalog.searchText("application deploy")` | ' +
|
|
14
|
+
'`catalog.getByTag("compose")` | `catalog.get("application.one")`. ' +
|
|
15
|
+
'Methods: searchText(query), get(procedure), getByTag(tag), endpoints, byTag.'),
|
|
16
16
|
})
|
|
17
17
|
.strict();
|
|
18
18
|
const searchCatalog = createSearchCatalogView();
|
|
@@ -49,10 +49,9 @@ export const searchTool = createTool({
|
|
|
49
49
|
name: 'search',
|
|
50
50
|
title: 'Search Dokploy API',
|
|
51
51
|
description: 'Search the Dokploy API catalog to discover procedures, parameters, and modules. ' +
|
|
52
|
-
'
|
|
53
|
-
'Use catalog.searchText("query") to find
|
|
54
|
-
'catalog.
|
|
55
|
-
'catalog.get("application.one") to get details of a specific procedure. ' +
|
|
52
|
+
'`catalog` is available as a global -- just write: `catalog.searchText("deploy")`. ' +
|
|
53
|
+
'Use catalog.searchText("query") to find by keyword, catalog.getByTag("application") for a module, ' +
|
|
54
|
+
'catalog.get("application.one") for one procedure. ' +
|
|
56
55
|
'Returns procedure names, required/optional parameters, and HTTP methods.',
|
|
57
56
|
schema: searchSchema,
|
|
58
57
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|