@openclaw/mxc-sandbox 2026.7.2-beta.7 → 2026.8.1-beta.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/dist/index.js +21 -12
- package/dist/mxc-spawn-launcher.mjs +30 -17
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
|
|
|
14
14
|
import { removePathWithinRoot, root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
15
15
|
import { FsSafeError, isPathInside } from "openclaw/plugin-sdk/security-runtime";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
17
18
|
import { execFileSync } from "node:child_process";
|
|
18
19
|
//#region extensions/mxc/src/config.ts
|
|
19
20
|
const MXC_CONTAINMENTS = ["process", "processcontainer"];
|
|
@@ -183,10 +184,10 @@ function createMxcFsBridge(params) {
|
|
|
183
184
|
var MxcFsBridge = class {
|
|
184
185
|
constructor(sandbox) {
|
|
185
186
|
this.sandbox = sandbox;
|
|
186
|
-
this.defaultContainerRoot = path.resolve(this.sandbox.containerWorkdir);
|
|
187
|
-
this.protectedSkillMounts = resolveMxcProtectedSkillMounts$1(this.sandbox);
|
|
188
|
-
this.workspaceMounts = resolveWorkspaceMounts(this.sandbox);
|
|
189
187
|
this.resolveRenameTargets = createWritableRenameTargetResolver((target) => this.resolveTarget(target), (target, action) => this.ensureWritable(target, action));
|
|
188
|
+
this.defaultContainerRoot = path.resolve(sandbox.containerWorkdir);
|
|
189
|
+
this.protectedSkillMounts = resolveMxcProtectedSkillMounts$1(sandbox);
|
|
190
|
+
this.workspaceMounts = resolveWorkspaceMounts(sandbox);
|
|
190
191
|
}
|
|
191
192
|
resolvePath(params) {
|
|
192
193
|
const target = this.resolveTarget(params);
|
|
@@ -929,7 +930,7 @@ function assertConfiguredPathExists(pathValue, source) {
|
|
|
929
930
|
} catch (err) {
|
|
930
931
|
if (isNodeError$1(err)) {
|
|
931
932
|
if (err.code === "ENOENT") throw new Error(`Sandbox policy path ${pathValue} configured by ${source} does not exist on the host. Create the path or update the policy file.`, { cause: err });
|
|
932
|
-
throw new Error(`Sandbox policy path ${pathValue} configured by ${source} is not accessible on the host: ${
|
|
933
|
+
throw new Error(`Sandbox policy path ${pathValue} configured by ${source} is not accessible on the host: ${formatErrorMessage(err)}`, { cause: err });
|
|
933
934
|
}
|
|
934
935
|
throw err;
|
|
935
936
|
}
|
|
@@ -942,7 +943,7 @@ function assertPositiveFiniteNumber(value, label) {
|
|
|
942
943
|
}
|
|
943
944
|
function policyFileError(policyPath, err) {
|
|
944
945
|
if (isNodeError$1(err) && err.code === "ENOENT") return new Error(`Configured sandbox policy file ${policyPath} does not exist. Remove it from mxcPolicyPaths or create the file.`, { cause: err });
|
|
945
|
-
return new Error(`Failed to load sandbox policy file at ${policyPath}: ${
|
|
946
|
+
return new Error(`Failed to load sandbox policy file at ${policyPath}: ${formatErrorMessage(err)}`, { cause: err instanceof Error ? err : void 0 });
|
|
946
947
|
}
|
|
947
948
|
function formatSandboxPolicyIssue(sourceLabel, issue) {
|
|
948
949
|
if (!issue) return `Sandbox policy at ${sourceLabel} is invalid.`;
|
|
@@ -965,10 +966,6 @@ function formatIssuePath(pathSegments) {
|
|
|
965
966
|
}
|
|
966
967
|
return label;
|
|
967
968
|
}
|
|
968
|
-
function formatError(err) {
|
|
969
|
-
if (err instanceof Error && err.message) return err.message;
|
|
970
|
-
return String(err);
|
|
971
|
-
}
|
|
972
969
|
function isNodeError$1(err) {
|
|
973
970
|
return err instanceof Error && "code" in err;
|
|
974
971
|
}
|
|
@@ -1026,7 +1023,7 @@ function resolveWorkdirInsideWorkspace(workspaceDir, workdir) {
|
|
|
1026
1023
|
try {
|
|
1027
1024
|
if (statSync(candidate).isDirectory()) return candidate;
|
|
1028
1025
|
} catch (err) {
|
|
1029
|
-
if (
|
|
1026
|
+
if (isMissingPathError(err)) throw new Error(`MXC sandbox workdir ${workdir} does not exist.`, { cause: err });
|
|
1030
1027
|
throw err;
|
|
1031
1028
|
}
|
|
1032
1029
|
throw new Error(`MXC sandbox workdir ${workdir} is not a directory.`);
|
|
@@ -1035,7 +1032,7 @@ function realpathForExistingPath(value, label) {
|
|
|
1035
1032
|
try {
|
|
1036
1033
|
return realpathSync(path.resolve(value));
|
|
1037
1034
|
} catch (err) {
|
|
1038
|
-
if (
|
|
1035
|
+
if (isMissingPathError(err)) throw new Error(`MXC ${label} ${value} does not exist.`, { cause: err });
|
|
1039
1036
|
throw err;
|
|
1040
1037
|
}
|
|
1041
1038
|
}
|
|
@@ -1044,7 +1041,7 @@ function realpathForPotentialPath(value) {
|
|
|
1044
1041
|
try {
|
|
1045
1042
|
return realpathSync(resolved);
|
|
1046
1043
|
} catch (err) {
|
|
1047
|
-
if (!
|
|
1044
|
+
if (!isMissingPathError(err)) throw err;
|
|
1048
1045
|
const parent = path.dirname(resolved);
|
|
1049
1046
|
if (parent === resolved) throw new Error(`MXC sandbox workdir ${value} does not exist.`, { cause: err });
|
|
1050
1047
|
return path.join(realpathForPotentialPath(parent), path.basename(resolved));
|
|
@@ -1053,6 +1050,9 @@ function realpathForPotentialPath(value) {
|
|
|
1053
1050
|
function isNodeError(err) {
|
|
1054
1051
|
return err instanceof Error && "code" in err;
|
|
1055
1052
|
}
|
|
1053
|
+
function isMissingPathError(err) {
|
|
1054
|
+
return isNodeError(err) && (err.code === "ENOENT" || err.code === "ENOTDIR");
|
|
1055
|
+
}
|
|
1056
1056
|
function buildMxcLauncherOptions(config, usePty) {
|
|
1057
1057
|
const options = {
|
|
1058
1058
|
debug: config.debug ?? false,
|
|
@@ -1087,6 +1087,15 @@ function createMxcSandboxBackendHandle(params) {
|
|
|
1087
1087
|
runtimeId: params.runtimeId,
|
|
1088
1088
|
runtimeLabel: params.runtimeId,
|
|
1089
1089
|
workdir: params.workdir,
|
|
1090
|
+
workdirValidation: "backend",
|
|
1091
|
+
async validateWorkdir(workdir) {
|
|
1092
|
+
try {
|
|
1093
|
+
return resolveWorkdirInsideWorkspace(params.workdir, workdir);
|
|
1094
|
+
} catch (err) {
|
|
1095
|
+
if (err instanceof Error && err.message.startsWith("MXC sandbox workdir")) return null;
|
|
1096
|
+
throw err;
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1090
1099
|
capabilities: {},
|
|
1091
1100
|
async buildExecSpec({ command, workdir, env, usePty }) {
|
|
1092
1101
|
const effectiveWorkdir = resolveWorkdirInsideWorkspace(params.workdir, workdir ?? params.workdir);
|
|
@@ -35,9 +35,9 @@ export function forwardSignals(spawned, options = {}) {
|
|
|
35
35
|
if (exitTimer) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
const setTimeoutFn = options.setTimeout ?? setTimeout;
|
|
38
|
+
const setTimeoutFn = options.setTimeout?.bind(undefined) ?? setTimeout;
|
|
39
39
|
exitTimer = setTimeoutFn(() => {
|
|
40
|
-
const exit = options.exit ?? process.exit;
|
|
40
|
+
const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code));
|
|
41
41
|
exit(signalExitCode(signal));
|
|
42
42
|
}, exitGraceMs);
|
|
43
43
|
exitTimer?.unref?.();
|
|
@@ -86,11 +86,37 @@ function bridgeChildProcess(child) {
|
|
|
86
86
|
|
|
87
87
|
export function exitOnChildProcessClose(child, options = {}) {
|
|
88
88
|
child.on("close", (exitCode, signal) => {
|
|
89
|
-
const exit = options.exit ?? process.exit;
|
|
89
|
+
const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code));
|
|
90
90
|
exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
function attachPtyProcess(spawned) {
|
|
95
|
+
bridgeStdio(spawned);
|
|
96
|
+
forwardSignals(spawned);
|
|
97
|
+
spawned.onExit(({ exitCode, signal }) => {
|
|
98
|
+
process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function attachChildProcess(spawned) {
|
|
103
|
+
bridgeChildProcess(spawned);
|
|
104
|
+
forwardSignals(spawned);
|
|
105
|
+
exitOnChildProcessClose(spawned);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function launchSandbox(spawnSandboxFromConfig, config, options, bridges = {}) {
|
|
109
|
+
// Normalize sync and Promise-returning SDK implementations before selecting an I/O bridge.
|
|
110
|
+
const spawned = await spawnSandboxFromConfig(config, options ?? {});
|
|
111
|
+
|
|
112
|
+
if (typeof spawned.onData === "function") {
|
|
113
|
+
(bridges.pty ?? attachPtyProcess)(spawned);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
(bridges.child ?? attachChildProcess)(spawned);
|
|
118
|
+
}
|
|
119
|
+
|
|
94
120
|
const SIGNAL_NUMBERS = new Map([
|
|
95
121
|
["SIGHUP", 1],
|
|
96
122
|
["SIGINT", 2],
|
|
@@ -123,20 +149,7 @@ export async function main() {
|
|
|
123
149
|
try {
|
|
124
150
|
const { config, options } = decodePayload(process.argv.slice(2));
|
|
125
151
|
const { spawnSandboxFromConfig } = await import("@microsoft/mxc-sdk");
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (typeof spawned.onData === "function") {
|
|
129
|
-
bridgeStdio(spawned);
|
|
130
|
-
forwardSignals(spawned);
|
|
131
|
-
spawned.onExit(({ exitCode, signal }) => {
|
|
132
|
-
process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
|
|
133
|
-
});
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
bridgeChildProcess(spawned);
|
|
138
|
-
forwardSignals(spawned);
|
|
139
|
-
exitOnChildProcessClose(spawned);
|
|
152
|
+
await launchSandbox(spawnSandboxFromConfig, config, options);
|
|
140
153
|
} catch (error) {
|
|
141
154
|
process.stderr.write(`${formatErrorStack(error)}\n`);
|
|
142
155
|
process.exit(127);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/mxc-sandbox",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.1-beta.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.
|
|
24
|
+
"pluginApi": ">=2026.8.1-beta.2"
|
|
25
25
|
},
|
|
26
26
|
"build": {
|
|
27
|
-
"openclawVersion": "2026.
|
|
27
|
+
"openclawVersion": "2026.8.1-beta.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.
|
|
50
|
+
"openclaw": ">=2026.8.1-beta.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"openclaw": {
|