@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 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: ${formatError(err)}`, { cause: err });
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}: ${formatError(err)}`, { cause: err instanceof Error ? err : void 0 });
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 (isNodeError(err) && err.code === "ENOENT") throw new Error(`MXC sandbox workdir ${workdir} does not exist.`, { cause: err });
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 (isNodeError(err) && err.code === "ENOENT") throw new Error(`MXC ${label} ${value} does not exist.`, { cause: err });
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 (!isNodeError(err) || err.code !== "ENOENT") throw err;
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
- const spawned = await spawnSandboxFromConfig(config, options ?? {});
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.7.2-beta.7",
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.7.2-beta.7"
24
+ "pluginApi": ">=2026.8.1-beta.2"
25
25
  },
26
26
  "build": {
27
- "openclawVersion": "2026.7.2-beta.7",
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.7.2-beta.7"
50
+ "openclaw": ">=2026.8.1-beta.2"
51
51
  },
52
52
  "peerDependenciesMeta": {
53
53
  "openclaw": {