@openclaw/mxc-sandbox 2026.9.1-beta.1 → 2026.9.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 +37 -103
- package/dist/index.js +21 -23
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -101,44 +101,36 @@ help stay in sync with plugin runtime validation.
|
|
|
101
101
|
- Windows filesystem-deny and host-list network policy knobs are not exposed by
|
|
102
102
|
this plugin until MXC can enforce them on ProcessContainer.
|
|
103
103
|
|
|
104
|
-
## Test setup
|
|
104
|
+
## Test setup
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
Use an already configured OpenClaw installation with MXC installed and enabled
|
|
107
|
+
on a supported Windows host. These commands create a uniquely named test agent
|
|
108
|
+
and a new temporary workspace, leaving existing agents and workspaces alone.
|
|
108
109
|
[`openclaw config patch --stdin`](https://docs.openclaw.ai/cli/config#config-patch)
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
applies sandbox settings only to that agent. Installation-wide MXC settings
|
|
111
|
+
and policy files remain unchanged; review them before testing because they
|
|
112
|
+
apply to the test agent too.
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
Run setup, testing, and cleanup in the same PowerShell session. Finish cleanup
|
|
115
|
+
before running setup again. Stop if any command fails.
|
|
114
116
|
|
|
115
117
|
```powershell
|
|
116
|
-
$
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
'@ | Set-Content -Path $mxcPolicyPath -Encoding utf8
|
|
118
|
+
$mxcAgentCreated = $false
|
|
119
|
+
$mxcAgent = "mxc-test-" + [guid]::NewGuid().ToString("N")
|
|
120
|
+
$mxcWorkspace = Join-Path ([System.IO.Path]::GetTempPath()) $mxcAgent
|
|
121
|
+
New-Item -ItemType Directory -Path $mxcWorkspace -ErrorAction Stop | Out-Null
|
|
122
|
+
|
|
123
|
+
openclaw agents add $mxcAgent `
|
|
124
|
+
--workspace $mxcWorkspace `
|
|
125
|
+
--non-interactive
|
|
126
|
+
if ($LASTEXITCODE -ne 0) { throw "Agent creation failed; stop without patching or deleting an existing agent." }
|
|
127
|
+
$mxcAgentCreated = $true
|
|
129
128
|
|
|
130
|
-
$mxcPolicyPathLiteral = ConvertTo-Json $mxcPolicyPath -Compress
|
|
131
129
|
$mxcConfigPatch = @"
|
|
132
130
|
{
|
|
133
131
|
agents: {
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
id: "main",
|
|
137
|
-
workspace: "~/.openclaw/workspace",
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
id: "mxc-test",
|
|
141
|
-
workspace: "~/.openclaw/workspace-mxc-test",
|
|
132
|
+
entries: {
|
|
133
|
+
"$mxcAgent": {
|
|
142
134
|
sandbox: {
|
|
143
135
|
mode: "all",
|
|
144
136
|
backend: "mxc",
|
|
@@ -146,62 +138,15 @@ $mxcConfigPatch = @"
|
|
|
146
138
|
workspaceAccess: "none",
|
|
147
139
|
},
|
|
148
140
|
},
|
|
149
|
-
],
|
|
150
|
-
},
|
|
151
|
-
plugins: {
|
|
152
|
-
entries: {
|
|
153
|
-
mxc: {
|
|
154
|
-
enabled: true,
|
|
155
|
-
config: {
|
|
156
|
-
containment: "process",
|
|
157
|
-
network: "none",
|
|
158
|
-
mxcPolicyPaths: [$mxcPolicyPathLiteral],
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
141
|
},
|
|
162
142
|
},
|
|
163
143
|
}
|
|
164
144
|
"@
|
|
165
145
|
|
|
166
146
|
$mxcConfigPatch | openclaw config patch --stdin --dry-run
|
|
147
|
+
if ($LASTEXITCODE -ne 0) { throw "Sandbox validation failed; use Cleanup to remove the new test agent." }
|
|
167
148
|
$mxcConfigPatch | openclaw config patch --stdin
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
Resulting config shape:
|
|
171
|
-
|
|
172
|
-
```jsonc
|
|
173
|
-
{
|
|
174
|
-
"agents": {
|
|
175
|
-
"list": [
|
|
176
|
-
{
|
|
177
|
-
"id": "main",
|
|
178
|
-
"workspace": "~/.openclaw/workspace",
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
"id": "mxc-test",
|
|
182
|
-
"workspace": "~/.openclaw/workspace-mxc-test",
|
|
183
|
-
"sandbox": {
|
|
184
|
-
"mode": "all",
|
|
185
|
-
"backend": "mxc",
|
|
186
|
-
"scope": "agent",
|
|
187
|
-
"workspaceAccess": "none",
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
"plugins": {
|
|
193
|
-
"entries": {
|
|
194
|
-
"mxc": {
|
|
195
|
-
"enabled": true,
|
|
196
|
-
"config": {
|
|
197
|
-
"containment": "process",
|
|
198
|
-
"network": "none",
|
|
199
|
-
"mxcPolicyPaths": ["C:\\Users\\you\\AppData\\Local\\Temp\\openclaw-mxc-policy.json"],
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
}
|
|
149
|
+
if ($LASTEXITCODE -ne 0) { throw "Sandbox setup failed; use Cleanup to remove the new test agent." }
|
|
205
150
|
```
|
|
206
151
|
|
|
207
152
|
## Sandbox policy files
|
|
@@ -283,44 +228,33 @@ ProcessContainer cannot safely enforce the nested read-only grant.
|
|
|
283
228
|
Run the TUI as that agent:
|
|
284
229
|
|
|
285
230
|
```powershell
|
|
286
|
-
openclaw tui --session agent:
|
|
231
|
+
openclaw tui --session "agent:${mxcAgent}:main"
|
|
287
232
|
```
|
|
288
233
|
|
|
289
234
|
For local embedded testing without a Gateway:
|
|
290
235
|
|
|
291
236
|
```powershell
|
|
292
|
-
openclaw tui --local --session agent:
|
|
237
|
+
openclaw tui --local --session "agent:${mxcAgent}:main"
|
|
293
238
|
```
|
|
294
239
|
|
|
295
240
|
## Cleanup
|
|
296
241
|
|
|
297
|
-
|
|
298
|
-
configuration by patching the config back to the default-only shape:
|
|
242
|
+
In the same PowerShell session, remove only the agent created by setup:
|
|
299
243
|
|
|
300
244
|
```powershell
|
|
301
|
-
$
|
|
302
|
-
|
|
303
|
-
agents: {
|
|
304
|
-
list: [
|
|
305
|
-
{
|
|
306
|
-
id: "main",
|
|
307
|
-
workspace: "~/.openclaw/workspace",
|
|
308
|
-
},
|
|
309
|
-
],
|
|
310
|
-
},
|
|
311
|
-
plugins: {
|
|
312
|
-
entries: {
|
|
313
|
-
mxc: null,
|
|
314
|
-
},
|
|
315
|
-
},
|
|
245
|
+
if (-not $mxcAgentCreated) {
|
|
246
|
+
throw "No successfully created test agent in this session; do not delete an existing agent."
|
|
316
247
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
$
|
|
320
|
-
$mxcCleanupPatch | openclaw config patch --stdin
|
|
321
|
-
Remove-Item -Path $mxcPolicyPath -ErrorAction SilentlyContinue
|
|
248
|
+
openclaw agents delete $mxcAgent --force
|
|
249
|
+
if ($LASTEXITCODE -ne 0) { throw "Agent cleanup failed; inspect the error before retrying." }
|
|
250
|
+
$mxcAgentCreated = $false
|
|
322
251
|
```
|
|
323
252
|
|
|
253
|
+
The delete command removes the test agent's configuration and attempts to move
|
|
254
|
+
its workspace and state to Trash. Check its output for any manual-cleanup
|
|
255
|
+
warning. Do not remove the MXC plugin entry or shared policy files: other
|
|
256
|
+
agents may still use them.
|
|
257
|
+
|
|
324
258
|
## Host readiness
|
|
325
259
|
|
|
326
260
|
IsoEnvBroker must be available on the host OS. The plugin checks this before
|
|
@@ -342,7 +276,7 @@ wxc-host-prep prepare-system-drive
|
|
|
342
276
|
pnpm test:extension mxc
|
|
343
277
|
```
|
|
344
278
|
|
|
345
|
-
`pnpm test extensions/mxc` is
|
|
279
|
+
`pnpm test extensions/mxc` is also supported for the bundled extension test lane.
|
|
346
280
|
|
|
347
281
|
For policy-only edits, the focused coverage is in:
|
|
348
282
|
|
package/dist/index.js
CHANGED
|
@@ -131,6 +131,12 @@ function resolveMxcBinaryPath(configOverride) {
|
|
|
131
131
|
return found;
|
|
132
132
|
}
|
|
133
133
|
//#endregion
|
|
134
|
+
//#region extensions/mxc/src/path-comparison.ts
|
|
135
|
+
function normalizeMxcPathForComparison(value) {
|
|
136
|
+
const resolved = path.resolve(value);
|
|
137
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
134
140
|
//#region extensions/mxc/src/workspace-skill-mounts.ts
|
|
135
141
|
const MATERIALIZED_SANDBOX_SKILLS_WORKSPACE_PARTS = [".openclaw", "sandbox-skills"];
|
|
136
142
|
function containerJoin(root, ...parts) {
|
|
@@ -299,7 +305,7 @@ function resolveWorkspaceMounts(sandbox) {
|
|
|
299
305
|
containerRoot,
|
|
300
306
|
writable: false
|
|
301
307
|
}];
|
|
302
|
-
if (sandbox.workspaceAccess === "ro" &&
|
|
308
|
+
if (sandbox.workspaceAccess === "ro" && normalizeMxcPathForComparison(agentWorkspaceDir) !== normalizeMxcPathForComparison(workspaceDir)) mounts.push({
|
|
303
309
|
hostRoot: agentWorkspaceDir,
|
|
304
310
|
containerRoot: agentWorkspaceDir,
|
|
305
311
|
writable: false
|
|
@@ -324,7 +330,7 @@ function normalizeMxcProtectedSkillMount(mount) {
|
|
|
324
330
|
function dedupeAndSortMounts(mounts) {
|
|
325
331
|
const deduped = /* @__PURE__ */ new Map();
|
|
326
332
|
for (const mount of mounts) {
|
|
327
|
-
const key = `${
|
|
333
|
+
const key = `${normalizeMxcPathForComparison(mount.hostRoot)}::${normalizeMxcPathForComparison(mount.containerRoot)}`;
|
|
328
334
|
if (!deduped.has(key)) deduped.set(key, mount);
|
|
329
335
|
}
|
|
330
336
|
return [...deduped.values()].toSorted((left, right) => {
|
|
@@ -338,11 +344,7 @@ function resolveRelativeParentPath(relativePath) {
|
|
|
338
344
|
return parent === "." || parent === "" ? null : parent;
|
|
339
345
|
}
|
|
340
346
|
function isSameMountRoot(first, second) {
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
function normalizePathForComparison$1(value) {
|
|
344
|
-
const resolved = path.resolve(value);
|
|
345
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
347
|
+
return normalizeMxcPathForComparison(first) === normalizeMxcPathForComparison(second);
|
|
346
348
|
}
|
|
347
349
|
//#endregion
|
|
348
350
|
//#region extensions/mxc/src/sandbox-baseline.ts
|
|
@@ -646,7 +648,7 @@ function resolveWorkspaceReadwritePathSpecs(workspace) {
|
|
|
646
648
|
function resolveWorkspaceReadonlyPathSpecs(workspace) {
|
|
647
649
|
if (workspace.workspaceAccess === "rw") return [];
|
|
648
650
|
const readonlyPathSpecs = [requiredFilesystemPath(workspace.workspaceDir)];
|
|
649
|
-
if (workspace.workspaceAccess === "ro" &&
|
|
651
|
+
if (workspace.workspaceAccess === "ro" && normalizeMxcPathForComparison(workspace.agentWorkspaceDir) !== normalizeMxcPathForComparison(workspace.workspaceDir)) readonlyPathSpecs.push(requiredFilesystemPath(workspace.agentWorkspaceDir));
|
|
650
652
|
return readonlyPathSpecs;
|
|
651
653
|
}
|
|
652
654
|
function resolveBaselineReadonlyPathSpecs(baseline, context) {
|
|
@@ -656,9 +658,9 @@ function resolveMxcProtectedSkillPolicyPaths(context) {
|
|
|
656
658
|
const deduped = /* @__PURE__ */ new Map();
|
|
657
659
|
for (const mount of resolveMxcProtectedSkillMounts(context)) {
|
|
658
660
|
const hostPath = path.resolve(mount.hostPath);
|
|
659
|
-
deduped.set(
|
|
661
|
+
deduped.set(normalizeMxcPathForComparison(hostPath), hostPath);
|
|
660
662
|
const containerPath = path.resolve(mount.containerPath);
|
|
661
|
-
deduped.set(
|
|
663
|
+
deduped.set(normalizeMxcPathForComparison(containerPath), containerPath);
|
|
662
664
|
}
|
|
663
665
|
return [...deduped.values()];
|
|
664
666
|
}
|
|
@@ -676,7 +678,7 @@ function resolveMxcProtectedSkillMounts(context) {
|
|
|
676
678
|
function resolveExistingFilesystemPaths(pathSpecs, accessLabel) {
|
|
677
679
|
const deduped = /* @__PURE__ */ new Map();
|
|
678
680
|
for (const pathSpec of pathSpecs) {
|
|
679
|
-
const key =
|
|
681
|
+
const key = normalizeMxcPathForComparison(pathSpec.path);
|
|
680
682
|
const existing = deduped.get(key);
|
|
681
683
|
if (existing) {
|
|
682
684
|
existing.required ||= pathSpec.required;
|
|
@@ -740,14 +742,10 @@ function assertNoMxcReadwriteReadonlyOverlap(params) {
|
|
|
740
742
|
for (const readwritePath of params.readwritePaths) for (const readonlyPath of params.readonlyPaths) if (pathsOverlap(readwritePath, readonlyPath)) throw new Error(`MXC readwrite path ${readwritePath} overlaps read-only path ${readonlyPath}. Windows MXC cannot safely enforce nested read-only overlays under writable paths.`);
|
|
741
743
|
}
|
|
742
744
|
function pathsOverlap(first, second) {
|
|
743
|
-
const left =
|
|
744
|
-
const right =
|
|
745
|
+
const left = normalizeMxcPathForComparison(first);
|
|
746
|
+
const right = normalizeMxcPathForComparison(second);
|
|
745
747
|
return isPathInside(left, right) || isPathInside(right, left);
|
|
746
748
|
}
|
|
747
|
-
function normalizePathForComparison(value) {
|
|
748
|
-
const resolved = path.resolve(value);
|
|
749
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
750
|
-
}
|
|
751
749
|
function hostPathExists(candidatePath) {
|
|
752
750
|
try {
|
|
753
751
|
statSync(candidatePath);
|
|
@@ -1180,8 +1178,8 @@ function createMxcSandboxBackendHandle(params) {
|
|
|
1180
1178
|
baseEnv: buildLauncherEnv(),
|
|
1181
1179
|
input: execInput,
|
|
1182
1180
|
maxOutputBytes: {
|
|
1183
|
-
stdout:
|
|
1184
|
-
stderr:
|
|
1181
|
+
stdout: 10485760,
|
|
1182
|
+
stderr: 10485760
|
|
1185
1183
|
},
|
|
1186
1184
|
signal: cmdParams.signal,
|
|
1187
1185
|
timeoutMs: 3e4
|
|
@@ -1329,11 +1327,11 @@ function registerMxcPlugin(api) {
|
|
|
1329
1327
|
factory: createMxcSandboxBackendFactory(config),
|
|
1330
1328
|
manager: mxcSandboxBackendManager
|
|
1331
1329
|
});
|
|
1332
|
-
api.
|
|
1330
|
+
api.lifecycle.registerRuntimeLifecycle({
|
|
1333
1331
|
id: "mxc-sandbox-cleanup",
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
unregister();
|
|
1332
|
+
cleanup: ({ reason, sessionKey, runId }) => {
|
|
1333
|
+
if (sessionKey !== void 0 || runId !== void 0) return;
|
|
1334
|
+
if (reason === "disable" || reason === "restart") unregister();
|
|
1337
1335
|
}
|
|
1338
1336
|
});
|
|
1339
1337
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/mxc-sandbox",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.2",
|
|
4
4
|
"description": "OpenClaw MXC sandbox execution plugin for MXC-capable hosts",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"minHostVersion": ">=2026.6.11"
|
|
22
22
|
},
|
|
23
23
|
"compat": {
|
|
24
|
-
"pluginApi": ">=2026.9.
|
|
24
|
+
"pluginApi": ">=2026.9.2"
|
|
25
25
|
},
|
|
26
26
|
"build": {
|
|
27
|
-
"openclawVersion": "2026.9.
|
|
27
|
+
"openclawVersion": "2026.9.2",
|
|
28
28
|
"bundledDist": false,
|
|
29
29
|
"staticAssets": [
|
|
30
30
|
{
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"README.md"
|
|
48
48
|
],
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"openclaw": ">=2026.9.
|
|
50
|
+
"openclaw": ">=2026.9.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"openclaw": {
|