mc8yp 2.6.0 → 2.6.2
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 +5 -3
- package/dist/cli.mjs +19 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -276,11 +276,13 @@ There is deliberately no raw-request escape hatch — the typed namespaces are t
|
|
|
276
276
|
|
|
277
277
|
Operations can be hidden from derivation and discovery by annotating them in the OpenAPI spec with the vendor extension `x-mc8yp-exclude: true` — with no escape hatch, exclusion is absolute for the sandbox.
|
|
278
278
|
|
|
279
|
-
### Sandbox surface (`sandbox`) — microservice mode only
|
|
279
|
+
### Sandbox surface (`sandbox`) — microservice mode only, opt-in
|
|
280
280
|
|
|
281
|
-
> **Experimental.** Available in deployed microservice mode
|
|
281
|
+
> **Experimental.** Available in deployed microservice mode only, and **disabled by default**; not exposed in the local CLI (agent harnesses there bring their own file I/O).
|
|
282
282
|
|
|
283
|
-
In microservice mode, codemode has one more global — `sandbox` — an in-memory shell with a virtual filesystem for wrangling data you fetched from the API (`jq`, `awk`, `sed`, `grep`, `sort`, `uniq`, `cut`, `sqlite3`, …). It has **no network access and no host filesystem access** — it never reaches Cumulocity. Fetch with `c8y`/service namespaces, process in the sandbox, read the result back.
|
|
283
|
+
In microservice mode, codemode has one more optional global — `sandbox` — an in-memory shell with a virtual filesystem for wrangling data you fetched from the API (`jq`, `awk`, `sed`, `grep`, `sort`, `uniq`, `cut`, `sqlite3`, …). It has **no network access and no host filesystem access** — it never reaches Cumulocity. Fetch with `c8y`/service namespaces, process in the sandbox, read the result back.
|
|
284
|
+
|
|
285
|
+
**It is off unless you turn it on.** Enable it per connection with the `mc8yp-enable-sandbox` header or the `enableSandbox` query param (any of empty, `*`, or `true`). Without it, `sandbox` is absent — same as CLI mode.
|
|
284
286
|
|
|
285
287
|
```js
|
|
286
288
|
async () => {
|
package/dist/cli.mjs
CHANGED
|
@@ -1546,7 +1546,7 @@ const consola = createConsola();
|
|
|
1546
1546
|
//#endregion
|
|
1547
1547
|
//#region package.json
|
|
1548
1548
|
var name = "mc8yp";
|
|
1549
|
-
var version = "2.6.
|
|
1549
|
+
var version = "2.6.2";
|
|
1550
1550
|
var description$1 = "Cumulocity IoT MCP Server - Model Context Protocol integration for IoT device management";
|
|
1551
1551
|
//#endregion
|
|
1552
1552
|
//#region \0virtual:core-openapi
|
|
@@ -58667,7 +58667,7 @@ function createCodeModeGuidePrompt() {
|
|
|
58667
58667
|
const namespaceNames = resolvedSpecs ? buildNamespaces(resolvedSpecs, restrictions, allowRules).map((ns) => ns.name) : ["c8y"];
|
|
58668
58668
|
const policyLines = [...restrictions.map((rule) => `- deny: \`${rule.source}\``), ...allowRules.map((rule) => `- allow: \`${rule.source}\``)];
|
|
58669
58669
|
const restrictionSection = policyLines.length > 0 ? `\n## Current Connection Access Policy\n${policyLines.join("\n")}\n\nOperations blocked by these rules are omitted from discovery (search/describe) entirely, and any live request that matches a deny rule (or misses the allow list) fails before reaching the tenant.\n` : "";
|
|
58670
|
-
const sandboxSection = c8yMcpServer.ctx.custom?.env === "server" ? `\n## Sandbox (scratch compute)
|
|
58670
|
+
const sandboxSection = c8yMcpServer.ctx.custom?.env === "server" && c8yMcpServer.ctx.custom?.enableSandbox ? `\n## Sandbox (scratch compute)
|
|
58671
58671
|
|
|
58672
58672
|
\`sandbox\` is your workspace: a persistent in-memory filesystem + Unix shell, separate from the API. It is where you keep files and process data. When the user asks to save or write a file, write it here (\`sandbox.writeFile\`) — never dump a file into the chat instead. Use the shell for text/data processing that is awkward in plain JS — \`jq\`, \`awk\`, \`sed\`, \`grep\`, \`sort\`, \`uniq\`, \`cut\`, \`sqlite3\`, etc. via \`sandbox.exec\`. It has NO network access and NO host filesystem access; it never reaches Cumulocity. Fetch data with \`c8y\`/namespaces, write it into the sandbox, process it, read the result back.
|
|
58673
58673
|
|
|
@@ -77814,7 +77814,7 @@ function armIdleTimer(sessionId) {
|
|
|
77814
77814
|
const session = sessions.get(sessionId);
|
|
77815
77815
|
if (!session) return;
|
|
77816
77816
|
if (session.timer) clearTimeout(session.timer);
|
|
77817
|
-
session.timer = setTimeout(() => evictSandboxSession(sessionId), IDLE_TTL_MS);
|
|
77817
|
+
session.timer = setTimeout(() => evictSandboxSession(sessionId, "idle-timeout"), IDLE_TTL_MS);
|
|
77818
77818
|
session.timer.unref?.();
|
|
77819
77819
|
}
|
|
77820
77820
|
function getSessionAdapter(sessionId) {
|
|
@@ -77833,22 +77833,24 @@ function resetSessionAdapter(sessionId) {
|
|
|
77833
77833
|
armIdleTimer(sessionId);
|
|
77834
77834
|
}
|
|
77835
77835
|
/**
|
|
77836
|
-
* Drop a session's sandbox and its timer.
|
|
77837
|
-
*
|
|
77836
|
+
* Drop a session's sandbox and its timer. Called by the clean-close (DELETE)
|
|
77837
|
+
* hook, the idle timer, and the process-exit teardown.
|
|
77838
77838
|
* @param sessionId - MCP session id.
|
|
77839
|
+
* @param reason - Why the sandbox is being dropped; included in the eviction log line.
|
|
77839
77840
|
*/
|
|
77840
|
-
function evictSandboxSession(sessionId) {
|
|
77841
|
+
function evictSandboxSession(sessionId, reason) {
|
|
77841
77842
|
const session = sessions.get(sessionId);
|
|
77842
77843
|
if (!session) return;
|
|
77843
77844
|
if (session.timer) clearTimeout(session.timer);
|
|
77844
77845
|
session.adapter.dispose?.();
|
|
77845
77846
|
sessions.delete(sessionId);
|
|
77847
|
+
consola.info(`[sandbox] evicted workspace for session ${sessionId} (reason: ${reason})`);
|
|
77846
77848
|
}
|
|
77847
77849
|
/**
|
|
77848
77850
|
* Evict every session (process exit, test cleanup).
|
|
77849
77851
|
*/
|
|
77850
77852
|
function disposeAllSandboxSessions() {
|
|
77851
|
-
for (const sessionId of [...sessions.keys()]) evictSandboxSession(sessionId);
|
|
77853
|
+
for (const sessionId of [...sessions.keys()]) evictSandboxSession(sessionId, "shutdown");
|
|
77852
77854
|
}
|
|
77853
77855
|
/**
|
|
77854
77856
|
* Build the `sandbox` host-module leaf for one codemode run: the full Flue
|
|
@@ -135160,7 +135162,7 @@ async function execute(functionCode) {
|
|
|
135160
135162
|
live = createUnauthenticatedCalls(error instanceof Error ? error.message : String(error));
|
|
135161
135163
|
}
|
|
135162
135164
|
const sessionId = c8yMcpServer.ctx.sessionId;
|
|
135163
|
-
const sandboxApi = c8yMcpServer.ctx.custom?.env === "server" && sessionId ? buildSandboxApi(sessionId) : void 0;
|
|
135165
|
+
const sandboxApi = c8yMcpServer.ctx.custom?.env === "server" && sessionId && c8yMcpServer.ctx.custom?.enableSandbox ? buildSandboxApi(sessionId) : void 0;
|
|
135164
135166
|
const result = await (await getSandbox()).run({
|
|
135165
135167
|
code: ENTRY_SOURCE,
|
|
135166
135168
|
filename: EXECUTE_ENTRY_PATH,
|
|
@@ -135185,16 +135187,19 @@ function getSafetyPreface(env) {
|
|
|
135185
135187
|
].join("\n");
|
|
135186
135188
|
}
|
|
135187
135189
|
/**
|
|
135188
|
-
* Purpose framing for the `sandbox` workspace. Server-only
|
|
135189
|
-
*
|
|
135190
|
-
*
|
|
135191
|
-
*
|
|
135192
|
-
*
|
|
135190
|
+
* Purpose framing for the `sandbox` workspace. Server-only, and disabled by
|
|
135191
|
+
* default — a connection must opt in (`mc8yp-enable-sandbox` header /
|
|
135192
|
+
* `enableSandbox` query param) before the `sandbox` global exists, so this
|
|
135193
|
+
* always-read tool description cannot assert it is present the way it can
|
|
135194
|
+
* for `codemode`/`docs`. CLI never has a sandbox regardless. This is
|
|
135195
|
+
* deliberately about PURPOSE (files/data processing live here), not
|
|
135196
|
+
* procedure — the observed failure was the agent not realizing the sandbox
|
|
135197
|
+
* is where files go and dumping output to chat.
|
|
135193
135198
|
* @param env - execution environment.
|
|
135194
135199
|
*/
|
|
135195
135200
|
function getSandboxNote(env) {
|
|
135196
135201
|
if (env !== "server") return "";
|
|
135197
|
-
return "\nYour workspace — `sandbox`: this
|
|
135202
|
+
return "\nYour workspace — `sandbox`: if this connection has it enabled (check `typeof sandbox !== 'undefined'`), it is a persistent in-memory filesystem + Unix shell, separate from the API, for keeping files and processing data. When the user asks to save or write a file, or when you need to filter/transform/aggregate fetched data (jq, awk, grep, sort, sqlite), do it here — `sandbox.writeFile(path, text)`, `sandbox.readFile(path)`, `sandbox.exec(command)` — never dump a file into the chat instead. Files persist across codemode calls in this session. Full method list: `codemode.describe(\"sandbox\")`.\n";
|
|
135198
135203
|
}
|
|
135199
135204
|
function createCodemodeTool(env) {
|
|
135200
135205
|
return defineTool({
|