@openclaw/mxc-sandbox 2026.9.7 → 2026.9.9
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 +32 -5
- package/dist/index.js +48 -23
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -38,7 +38,9 @@ readiness behavior to change as MXC host support matures.
|
|
|
38
38
|
|
|
39
39
|
- Plugin id: `mxc`
|
|
40
40
|
- Package: `@openclaw/mxc-sandbox`
|
|
41
|
-
- Minimum OpenClaw host: `2026.6.11`
|
|
41
|
+
- Minimum OpenClaw install host: `2026.6.11`
|
|
42
|
+
- Plugin API host requirement: `2026.9.6` or newer. An older host cannot run
|
|
43
|
+
the current plugin even if the MXC executor is updated.
|
|
42
44
|
|
|
43
45
|
## Plugin config
|
|
44
46
|
|
|
@@ -48,7 +50,7 @@ and out-of-range values fail plugin activation with an actionable error
|
|
|
48
50
|
|
|
49
51
|
| Field | Type | Default | Notes |
|
|
50
52
|
| ---------------- | --------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
-
| `mxcBinaryPath` | `string` | unset | Non-empty override for
|
|
53
|
+
| `mxcBinaryPath` | `string` | unset | Non-empty override for a compatible `wxc-exec.exe` supporting `--probe`; see [Host readiness](#host-readiness). |
|
|
52
54
|
| `containment` | `"process" \| "processcontainer"` | `"process"` | Both currently resolve to Windows ProcessContainer. |
|
|
53
55
|
| `network` | `"none" \| "default"` | `"none"` | `"default"` allows outbound network via the `internetClient` capability. |
|
|
54
56
|
| `timeoutSeconds` | `number` | unset (baseline default `300` applies) | Must be `>= 1` and `<= 2147000` (the largest Node-safe `setTimeout` delay in whole seconds). Capped to the sandbox policy baseline timeout when both are set. |
|
|
@@ -86,7 +88,7 @@ help stay in sync with plugin runtime validation.
|
|
|
86
88
|
- OpenClaw passes per-run command, environment, and filesystem config to the
|
|
87
89
|
plugin's Node launcher through a short-lived local payload file, and deletes
|
|
88
90
|
that file and its temp directory when the launcher or run finishes.
|
|
89
|
-
- `@microsoft/mxc-sdk@0.
|
|
91
|
+
- `@microsoft/mxc-sdk@0.8.0` then carries the full base64 request envelope on
|
|
90
92
|
the native `wxc-exec` process argv. A host user with process-inspection rights
|
|
91
93
|
can observe that command, environment, and policy data while the process is
|
|
92
94
|
running. Do not put secrets in MXC command arguments or environment values
|
|
@@ -257,8 +259,33 @@ agents may still use them.
|
|
|
257
259
|
|
|
258
260
|
## Host readiness
|
|
259
261
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
Before registering the sandbox backend, the plugin runs the MXC executor's own
|
|
263
|
+
host probe (`wxc-exec --probe`) and requires it to select an isolation tier
|
|
264
|
+
(`base-container`, `appcontainer-bfs`, or `appcontainer-dacl`). It probes the
|
|
265
|
+
same executor it launches, including an `mxcBinaryPath` override. MXC's tier
|
|
266
|
+
degradation warnings are logged but do not block activation. To inspect a host,
|
|
267
|
+
run the executor directly:
|
|
268
|
+
|
|
269
|
+
```powershell
|
|
270
|
+
& node_modules\@microsoft\mxc-sdk\bin\x64\wxc-exec.exe --probe
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Use `bin\arm64` on Arm64 hosts.
|
|
274
|
+
|
|
275
|
+
An existing `mxcBinaryPath` override must point to an MXC 0.8.0-compatible
|
|
276
|
+
executor that supports `--probe`. An older executor stops plugin activation;
|
|
277
|
+
the plugin cannot safely infer readiness from a Windows service name or probe a
|
|
278
|
+
different binary. If the override fails, run that exact executable with
|
|
279
|
+
`--probe` to see its error. Update the override to a compatible executor, or
|
|
280
|
+
remove it to use the `@microsoft/mxc-sdk@0.8.0` executor installed with the
|
|
281
|
+
plugin:
|
|
282
|
+
|
|
283
|
+
```powershell
|
|
284
|
+
openclaw config unset plugins.entries.mxc.config.mxcBinaryPath
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Restart the Gateway after changing the override. If the SDK executor also
|
|
288
|
+
fails `--probe`, address the reported host-readiness error before retrying.
|
|
262
289
|
|
|
263
290
|
Host preparation is advisory. If directory listing inside the sandbox fails with
|
|
264
291
|
`Access is denied`, run this once from an elevated prompt:
|
package/dist/index.js
CHANGED
|
@@ -509,7 +509,6 @@ function buildLauncherEnv(hostEnv = process.env) {
|
|
|
509
509
|
//#endregion
|
|
510
510
|
//#region extensions/mxc/src/mxc-container-config.ts
|
|
511
511
|
const MXC_SCHEMA_VERSION = "0.7.0-alpha";
|
|
512
|
-
const PROCESS_CONTAINER_NAME_MAX_LEN = 64;
|
|
513
512
|
function resolveCurrentBaselineContext(projectDir) {
|
|
514
513
|
return {
|
|
515
514
|
projectDir: path.resolve(projectDir),
|
|
@@ -559,7 +558,10 @@ function buildMxcContainerConfig(params) {
|
|
|
559
558
|
version: MXC_SCHEMA_VERSION,
|
|
560
559
|
containerId: params.containerId,
|
|
561
560
|
containment: params.config.containment,
|
|
562
|
-
lifecycle: {
|
|
561
|
+
lifecycle: {
|
|
562
|
+
destroyOnExit: true,
|
|
563
|
+
preservePolicy: false
|
|
564
|
+
},
|
|
563
565
|
process: {
|
|
564
566
|
commandLine: buildCommandLine(params.command, params.args ?? []),
|
|
565
567
|
cwd: params.workdir,
|
|
@@ -577,7 +579,6 @@ function buildMxcContainerConfig(params) {
|
|
|
577
579
|
enforcementMode: "capabilities"
|
|
578
580
|
},
|
|
579
581
|
processContainer: {
|
|
580
|
-
name: processContainerName(params.runtimeId),
|
|
581
582
|
leastPrivilege: true,
|
|
582
583
|
capabilities: networkAllowed ? ["internetClient"] : [],
|
|
583
584
|
ui: {
|
|
@@ -617,8 +618,7 @@ function buildFilesystemConfig(params) {
|
|
|
617
618
|
return {
|
|
618
619
|
readonlyPaths,
|
|
619
620
|
deniedPaths: void 0,
|
|
620
|
-
readwritePaths
|
|
621
|
-
clearPolicyOnExit: true
|
|
621
|
+
readwritePaths
|
|
622
622
|
};
|
|
623
623
|
}
|
|
624
624
|
function resolveWorkspaceReadwritePathSpecs(workspace) {
|
|
@@ -698,11 +698,6 @@ function buildMissingFilesystemPathMessage(pathValue, accessLabel, sources) {
|
|
|
698
698
|
if (sourceLabel) return `MXC sandbox ${accessLabel} path ${pathValue} configured by ${sourceLabel} is missing on the host. Recreate the path or update the policy file before launching the sandbox.`;
|
|
699
699
|
return `MXC sandbox ${accessLabel} path ${pathValue} does not exist on the host.`;
|
|
700
700
|
}
|
|
701
|
-
function processContainerName(runtimeId) {
|
|
702
|
-
if (runtimeId.length <= PROCESS_CONTAINER_NAME_MAX_LEN) return runtimeId;
|
|
703
|
-
const hash = createHash("sha256").update(runtimeId).digest("hex").slice(0, 8);
|
|
704
|
-
return `${runtimeId.slice(0, PROCESS_CONTAINER_NAME_MAX_LEN - hash.length - 1)}-${hash}`;
|
|
705
|
-
}
|
|
706
701
|
function resolveProcessTimeoutSeconds(config, baseline) {
|
|
707
702
|
if (config.timeoutSecondsConfigured === true) return Math.min(config.timeoutSeconds, baseline.process.timeoutSeconds);
|
|
708
703
|
return baseline.process.timeoutSeconds;
|
|
@@ -904,7 +899,7 @@ function isNodeError(err) {
|
|
|
904
899
|
}
|
|
905
900
|
//#endregion
|
|
906
901
|
//#region extensions/mxc/src/mxc-backend.ts
|
|
907
|
-
const CONTAINER_ID_MAX_LEN =
|
|
902
|
+
const CONTAINER_ID_MAX_LEN = 64;
|
|
908
903
|
function uniqueContainerId(runtimeId) {
|
|
909
904
|
const suffix = randomBytes(4).toString("hex");
|
|
910
905
|
return `${runtimeId.length + suffix.length + 1 > CONTAINER_ID_MAX_LEN ? runtimeId.slice(0, CONTAINER_ID_MAX_LEN - suffix.length - 1) : runtimeId}-${suffix}`;
|
|
@@ -1028,7 +1023,6 @@ function createMxcSandboxBackendHandle(params) {
|
|
|
1028
1023
|
config: params.config,
|
|
1029
1024
|
baseline,
|
|
1030
1025
|
baselineContext,
|
|
1031
|
-
runtimeId: params.runtimeId,
|
|
1032
1026
|
containerId: uniqueContainerId(params.runtimeId),
|
|
1033
1027
|
command,
|
|
1034
1028
|
sandboxTempDir,
|
|
@@ -1083,7 +1077,6 @@ function createMxcSandboxBackendHandle(params) {
|
|
|
1083
1077
|
config: restrictiveConfig,
|
|
1084
1078
|
baseline,
|
|
1085
1079
|
baselineContext,
|
|
1086
|
-
runtimeId: params.runtimeId,
|
|
1087
1080
|
containerId: uniqueContainerId(params.runtimeId),
|
|
1088
1081
|
command: commandBridge.command,
|
|
1089
1082
|
args: cmdParams.args,
|
|
@@ -1173,22 +1166,46 @@ function createMxcSandboxBackendFactory(config) {
|
|
|
1173
1166
|
//#endregion
|
|
1174
1167
|
//#region extensions/mxc/src/readiness.ts
|
|
1175
1168
|
const DEFAULT_DEPS = { execFileSync };
|
|
1169
|
+
const MxcProbeOutputSchema = z.object({
|
|
1170
|
+
tier: z.enum([
|
|
1171
|
+
"base-container",
|
|
1172
|
+
"appcontainer-bfs",
|
|
1173
|
+
"appcontainer-dacl"
|
|
1174
|
+
]).optional(),
|
|
1175
|
+
warnings: z.array(z.string()).default([]),
|
|
1176
|
+
error: z.string().optional()
|
|
1177
|
+
});
|
|
1176
1178
|
function resolveWindowsSystemExecutable(name) {
|
|
1177
1179
|
const systemRoot = process.env.SystemRoot || process.env.WINDIR;
|
|
1178
1180
|
return path.win32.join(systemRoot || "C:\\Windows", "System32", name);
|
|
1179
1181
|
}
|
|
1180
|
-
function
|
|
1182
|
+
function probeMxcIsolationTier(executablePath, deps) {
|
|
1183
|
+
const notReady = (reason, cause) => new Error(`[mxc] MXC Windows ProcessContainer sandbox is not ready: ${reason}. Run "${executablePath}" --probe for host details. The selected executor must be compatible with MXC 0.8.0 and support --probe. If mxcBinaryPath points to an older executor, update it or unset plugins.entries.mxc.config.mxcBinaryPath and restart the Gateway to use the bundled SDK executor.`, cause === void 0 ? void 0 : { cause });
|
|
1184
|
+
let output;
|
|
1181
1185
|
try {
|
|
1182
|
-
deps.execFileSync(
|
|
1186
|
+
output = deps.execFileSync(executablePath, ["--probe"], {
|
|
1183
1187
|
encoding: "utf-8",
|
|
1184
1188
|
stdio: "pipe",
|
|
1185
|
-
timeout:
|
|
1189
|
+
timeout: 15e3,
|
|
1186
1190
|
windowsHide: true
|
|
1187
1191
|
});
|
|
1188
1192
|
} catch (error) {
|
|
1189
|
-
|
|
1190
|
-
throw new Error(`[mxc] MXC Windows ProcessContainer sandbox is not ready: IsoEnvBroker service is not installed${detail}. Install the IsoEnvBroker service before enabling MXC sandbox execution.`, { cause: error });
|
|
1193
|
+
throw notReady(`the MXC host probe failed${error instanceof Error && error.message ? `: ${error.message.trim()}` : ""}`, error);
|
|
1191
1194
|
}
|
|
1195
|
+
let probe;
|
|
1196
|
+
try {
|
|
1197
|
+
probe = JSON.parse(output);
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
throw notReady("the MXC host probe did not return JSON", error);
|
|
1200
|
+
}
|
|
1201
|
+
const parsed = MxcProbeOutputSchema.safeParse(probe);
|
|
1202
|
+
if (!parsed.success) throw notReady("the MXC host probe returned an unexpected result", parsed.error);
|
|
1203
|
+
const { tier, warnings, error } = parsed.data;
|
|
1204
|
+
if (!tier) throw notReady(`MXC cannot select an isolation tier on this host (${error || "the probe reported no isolation tier"})`);
|
|
1205
|
+
return {
|
|
1206
|
+
tier,
|
|
1207
|
+
warnings
|
|
1208
|
+
};
|
|
1192
1209
|
}
|
|
1193
1210
|
function isSystemDrivePrepared(deps) {
|
|
1194
1211
|
const systemDrive = process.env.SystemDrive || "C:";
|
|
@@ -1219,12 +1236,19 @@ function warnMxcHostPrepIfNeeded(params = {}) {
|
|
|
1219
1236
|
...params.deps
|
|
1220
1237
|
})) (params.warn ?? ((message) => console.warn(message)))(systemDrivePrepWarning(process.env.SystemDrive || "C:"));
|
|
1221
1238
|
}
|
|
1222
|
-
|
|
1239
|
+
/**
|
|
1240
|
+
* Fails plugin activation unless MXC's host probe selects an isolation tier for
|
|
1241
|
+
* `executablePath`. Degradation warnings from the probe are reported but do not
|
|
1242
|
+
* block activation.
|
|
1243
|
+
*/
|
|
1244
|
+
function assertMxcReadiness(params) {
|
|
1223
1245
|
if ((params.platform ?? process.platform) !== "win32") return;
|
|
1224
|
-
|
|
1246
|
+
const deps = {
|
|
1225
1247
|
...DEFAULT_DEPS,
|
|
1226
1248
|
...params.deps
|
|
1227
|
-
}
|
|
1249
|
+
};
|
|
1250
|
+
const probe = probeMxcIsolationTier(params.executablePath, deps);
|
|
1251
|
+
if (probe.warnings.length > 0) (params.warn ?? ((message) => console.warn(message)))(`[mxc] MXC sandbox is using the ${probe.tier} isolation tier: ${probe.warnings.join("; ")}`);
|
|
1228
1252
|
}
|
|
1229
1253
|
//#endregion
|
|
1230
1254
|
//#region extensions/mxc/src/plugin.ts
|
|
@@ -1235,13 +1259,14 @@ function registerMxcPlugin(api) {
|
|
|
1235
1259
|
console.warn(`[mxc] Sandbox backend is Windows-only and not available on ${process.platform}. Plugin will be dormant.`);
|
|
1236
1260
|
return;
|
|
1237
1261
|
}
|
|
1262
|
+
let mxcBinaryPath;
|
|
1238
1263
|
try {
|
|
1239
|
-
resolveMxcBinaryPath(config.mxcBinaryPath);
|
|
1264
|
+
mxcBinaryPath = resolveMxcBinaryPath(config.mxcBinaryPath);
|
|
1240
1265
|
} catch (err) {
|
|
1241
1266
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1242
1267
|
throw new Error(`[mxc] MXC sandbox backend cannot load: ${reason}. Install @microsoft/mxc-sdk or set mxcBinaryPath.`, { cause: err });
|
|
1243
1268
|
}
|
|
1244
|
-
assertMxcReadiness();
|
|
1269
|
+
assertMxcReadiness({ executablePath: mxcBinaryPath });
|
|
1245
1270
|
warnMxcHostPrepIfNeeded();
|
|
1246
1271
|
const unregister = registerSandboxBackend("mxc", {
|
|
1247
1272
|
factory: createMxcSandboxBackendFactory(config),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/mxc-sandbox",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.9",
|
|
4
4
|
"description": "OpenClaw MXC sandbox execution plugin for MXC-capable hosts",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"minHostVersion": ">=2026.6.11"
|
|
23
23
|
},
|
|
24
24
|
"compat": {
|
|
25
|
-
"pluginApi": ">=2026.9.
|
|
25
|
+
"pluginApi": ">=2026.9.9"
|
|
26
26
|
},
|
|
27
27
|
"build": {
|
|
28
|
-
"openclawVersion": "2026.9.
|
|
28
|
+
"openclawVersion": "2026.9.9",
|
|
29
29
|
"bundledDist": false,
|
|
30
30
|
"staticAssets": [
|
|
31
31
|
{
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"assets/activity.svg"
|
|
51
51
|
],
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"openclaw": ">=2026.9.
|
|
53
|
+
"openclaw": ">=2026.9.9"
|
|
54
54
|
},
|
|
55
55
|
"peerDependenciesMeta": {
|
|
56
56
|
"openclaw": {
|