@vibetools/dokploy-mcp 2.0.1 → 2.1.1
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,10 +11,11 @@ const executeSchema = z
|
|
|
11
11
|
code: z
|
|
12
12
|
.string()
|
|
13
13
|
.min(1)
|
|
14
|
-
.describe('
|
|
15
|
-
'
|
|
14
|
+
.describe('JavaScript code. `dokploy` and `helpers` are globals -- do NOT wrap in a function. ' +
|
|
15
|
+
'Simple: `await dokploy.project.all()`. ' +
|
|
16
|
+
'Multi-step: `const app = await dokploy.application.one({ applicationId: "abc" }); return app.name`. ' +
|
|
16
17
|
'dokploy.<module>.<method>(params) calls the Dokploy API. ' +
|
|
17
|
-
'helpers: sleep(ms), assert(cond, msg), pick(obj, keys), limit(arr, n)
|
|
18
|
+
'helpers: sleep(ms), assert(cond, msg), pick(obj, keys), limit(arr, n).'),
|
|
18
19
|
})
|
|
19
20
|
.strict();
|
|
20
21
|
export function buildExecuteContext(executor, maxCalls = resolveSandboxLimits().maxCalls) {
|
|
@@ -48,13 +49,13 @@ export async function runExecuteWithHost(code, host) {
|
|
|
48
49
|
export const executeTool = createTool({
|
|
49
50
|
name: 'execute',
|
|
50
51
|
title: 'Execute Dokploy Workflow',
|
|
51
|
-
description: '
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'deployment, docker, server, settings, user, notification, backup, mounts, registry, certificates, and more. ' +
|
|
57
|
-
'Use search tool first to discover
|
|
52
|
+
description: 'Run JavaScript code against the Dokploy API. ' +
|
|
53
|
+
'IMPORTANT: Do NOT wrap code in a function -- `dokploy` and `helpers` are already globals. ' +
|
|
54
|
+
'Write bare code: `await dokploy.project.all()` or `const x = await dokploy.application.one({ applicationId: "id" }); return x`. ' +
|
|
55
|
+
'dokploy.<module>.<method>(params) calls the API. ' +
|
|
56
|
+
'Modules: project, environment, application, compose, domain, postgres, mysql, mariadb, mongo, redis, ' +
|
|
57
|
+
'deployment, docker, server, settings, user, notification, backup, mounts, registry, certificates, schedule, patch, sshKey, gitProvider, and more. ' +
|
|
58
|
+
'Use search tool first to discover procedure names and required parameters.',
|
|
58
59
|
schema: executeSchema,
|
|
59
60
|
annotations: { openWorldHint: true },
|
|
60
61
|
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. `catalog` is a global -- do NOT wrap in a function. ' +
|
|
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();
|
|
@@ -48,11 +48,9 @@ function trimArrayToBytes(items) {
|
|
|
48
48
|
export const searchTool = createTool({
|
|
49
49
|
name: 'search',
|
|
50
50
|
title: 'Search Dokploy API',
|
|
51
|
-
description: 'Search the Dokploy API catalog
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'catalog.getByTag("application") to list all procedures in a module, ' +
|
|
55
|
-
'catalog.get("application.one") to get details of a specific procedure. ' +
|
|
51
|
+
description: 'Search the Dokploy API catalog. ' +
|
|
52
|
+
'IMPORTANT: Do NOT wrap code in a function -- `catalog` is already a global. ' +
|
|
53
|
+
'Write bare code: `catalog.searchText("deploy")` or `catalog.getByTag("application")` or `catalog.get("application.one")`. ' +
|
|
56
54
|
'Returns procedure names, required/optional parameters, and HTTP methods.',
|
|
57
55
|
schema: searchSchema,
|
|
58
56
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|