@openclaw/openshell-sandbox 2026.7.1-beta.1 → 2026.7.1-beta.4

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
@@ -1,6 +1,6 @@
1
1
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2
2
  import { buildRemoteWorkdirValidationCommand, buildValidatedExecRemoteCommand, createRemoteShellSandboxFsBridge, createSshSandboxSessionFromConfigText, createWritableRenameTargetResolver, disposeSshSandboxSession, registerSandboxBackend, resolvePreferredOpenClawTmpDir, runPluginCommandWithTimeout, runSshSandboxCommand, sanitizeEnvVars, shellEscape, withTempWorkspace } from "openclaw/plugin-sdk/sandbox";
3
- import fs from "node:fs/promises";
3
+ import fsPromises from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { buildPluginConfigSchema } from "openclaw/plugin-sdk/core";
@@ -283,7 +283,7 @@ var OpenShellFsBridge = class {
283
283
  async stat(params) {
284
284
  const target = this.resolveTarget(params);
285
285
  const hostPath = this.requireHostPath(target);
286
- const stats = await fs.lstat(hostPath).catch(() => null);
286
+ const stats = await fsPromises.lstat(hostPath).catch(() => null);
287
287
  if (!stats) return null;
288
288
  await assertLocalPathSafety({
289
289
  target,
@@ -400,9 +400,9 @@ async function removeLocalRootPath(params) {
400
400
  const root$1 = await root(params.target.mountHostRoot);
401
401
  const relativePath = relativeToRoot(params.target, params.hostPath);
402
402
  try {
403
- if (params.force === false) await fs.lstat(params.hostPath);
403
+ if (params.force === false) await fsPromises.lstat(params.hostPath);
404
404
  if (params.recursive) {
405
- if ((await fs.lstat(params.hostPath).catch((err) => {
405
+ if ((await fsPromises.lstat(params.hostPath).catch((err) => {
406
406
  if (isNotFoundError(err)) return null;
407
407
  throw err;
408
408
  }))?.isSymbolicLink()) {
@@ -444,12 +444,12 @@ function relativeToRoot(target, hostPath) {
444
444
  return relativePath === "." ? "" : relativePath;
445
445
  }
446
446
  async function assertRenameSourceSupported(fromHostPath) {
447
- const stats = await fs.lstat(fromHostPath);
447
+ const stats = await fsPromises.lstat(fromHostPath);
448
448
  if (stats.isSymbolicLink()) throw new Error("Sandbox symlink rename sources are not supported by the local mirror bridge");
449
449
  if (stats.isFile() && stats.nlink > 1) throw new Error("Sandbox hardlinked rename sources are not supported by the local mirror bridge");
450
450
  }
451
451
  async function assertSameDeviceRenameSupported(params) {
452
- const sourceStats = await fs.lstat(params.fromHostPath);
452
+ const sourceStats = await fsPromises.lstat(params.fromHostPath);
453
453
  const destinationParentStats = await nearestExistingDirectoryStats({
454
454
  root: params.root,
455
455
  targetPath: path.dirname(params.toHostPath)
@@ -460,7 +460,7 @@ async function nearestExistingDirectoryStats(params) {
460
460
  const rootPath = path.resolve(params.root);
461
461
  let cursor = path.resolve(params.targetPath);
462
462
  while (isPathInside(rootPath, cursor)) {
463
- const stats = await fs.lstat(cursor).catch((err) => {
463
+ const stats = await fsPromises.lstat(cursor).catch((err) => {
464
464
  if (isNotFoundError(err)) return null;
465
465
  throw err;
466
466
  });
@@ -472,7 +472,7 @@ async function nearestExistingDirectoryStats(params) {
472
472
  if (next === cursor) break;
473
473
  cursor = next;
474
474
  }
475
- return await fs.lstat(rootPath);
475
+ return await fsPromises.lstat(rootPath);
476
476
  }
477
477
  function isNotFoundError(err) {
478
478
  return err instanceof FsSafeError && err.code === "not-found" || typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
@@ -511,15 +511,15 @@ function resolveProtectedSkillShadowTarget(params) {
511
511
  }
512
512
  async function assertLocalPathSafety(params) {
513
513
  if (!params.target.hostPath) throw new Error(`Missing local host path for ${params.target.containerPath}`);
514
- const canonicalRoot = await fs.realpath(params.root).catch(() => path.resolve(params.root));
515
- const targetStats = await fs.lstat(params.target.hostPath).catch(() => null);
514
+ const canonicalRoot = await fsPromises.realpath(params.root).catch(() => path.resolve(params.root));
515
+ const targetStats = await fsPromises.lstat(params.target.hostPath).catch(() => null);
516
516
  if (!isPathInside(canonicalRoot, params.allowFinalSymlinkForUnlink && targetStats?.isSymbolicLink() ? path.resolve(canonicalRoot, path.relative(params.root, params.target.hostPath)) : await resolveCanonicalCandidate(params.target.hostPath))) throw new Error(`Sandbox path escapes allowed mounts; cannot access: ${params.target.containerPath}`);
517
517
  const relative = path.relative(params.root, params.target.hostPath);
518
518
  const segments = relative.split(path.sep).filter(Boolean).slice(0, Math.max(0, relative.split(path.sep).filter(Boolean).length));
519
519
  let cursor = params.root;
520
520
  for (let index = 0; index < segments.length; index += 1) {
521
521
  cursor = path.join(cursor, segments[index]);
522
- const stats = await fs.lstat(cursor).catch(() => null);
522
+ const stats = await fsPromises.lstat(cursor).catch(() => null);
523
523
  if (!stats) {
524
524
  if (index === segments.length - 1 && params.allowMissingLeaf) return;
525
525
  continue;
@@ -532,8 +532,8 @@ async function resolveCanonicalCandidate(targetPath) {
532
532
  const missing = [];
533
533
  let cursor = path.resolve(targetPath);
534
534
  while (true) {
535
- if (await fs.lstat(cursor).then(() => true).catch(() => false)) {
536
- const canonical = await fs.realpath(cursor).catch(() => cursor);
535
+ if (await fsPromises.lstat(cursor).then(() => true).catch(() => false)) {
536
+ const canonical = await fsPromises.realpath(cursor).catch(() => cursor);
537
537
  return path.resolve(canonical, ...missing);
538
538
  }
539
539
  const parent = path.dirname(cursor);
@@ -575,16 +575,16 @@ function createConcurrencyLimiter(limit) {
575
575
  }
576
576
  const runLimitedFs = createConcurrencyLimiter(COPY_TREE_FS_CONCURRENCY);
577
577
  async function lstatIfExists(targetPath) {
578
- return await runLimitedFs(async () => await fs.lstat(targetPath)).catch(() => null);
578
+ return await runLimitedFs(async () => await fsPromises.lstat(targetPath)).catch(() => null);
579
579
  }
580
580
  async function copyTreeWithoutSymlinks(params) {
581
- const stats = await runLimitedFs(async () => await fs.lstat(params.sourcePath));
581
+ const stats = await runLimitedFs(async () => await fsPromises.lstat(params.sourcePath));
582
582
  if (stats.isSymbolicLink()) return;
583
583
  const targetStats = await lstatIfExists(params.targetPath);
584
584
  if (params.preserveTargetSymlinks && targetStats?.isSymbolicLink()) return;
585
585
  if (stats.isDirectory()) {
586
- await runLimitedFs(async () => await fs.mkdir(params.targetPath, { recursive: true }));
587
- const entries = await runLimitedFs(async () => await fs.readdir(params.sourcePath));
586
+ await runLimitedFs(async () => await fsPromises.mkdir(params.targetPath, { recursive: true }));
587
+ const entries = await runLimitedFs(async () => await fsPromises.readdir(params.sourcePath));
588
588
  await Promise.all(entries.map(async (entry) => {
589
589
  await copyTreeWithoutSymlinks({
590
590
  sourcePath: path.join(params.sourcePath, entry),
@@ -595,23 +595,23 @@ async function copyTreeWithoutSymlinks(params) {
595
595
  return;
596
596
  }
597
597
  if (stats.isFile()) {
598
- await runLimitedFs(async () => await fs.mkdir(path.dirname(params.targetPath), { recursive: true }));
599
- await runLimitedFs(async () => await fs.copyFile(params.sourcePath, params.targetPath));
598
+ await runLimitedFs(async () => await fsPromises.mkdir(path.dirname(params.targetPath), { recursive: true }));
599
+ await runLimitedFs(async () => await fsPromises.copyFile(params.sourcePath, params.targetPath));
600
600
  }
601
601
  }
602
602
  async function replaceDirectoryContents(params) {
603
603
  const isExcluded = createExcludeMatcher(params.excludeDirs);
604
- await fs.mkdir(params.targetDir, { recursive: true });
605
- const existing = await fs.readdir(params.targetDir);
604
+ await fsPromises.mkdir(params.targetDir, { recursive: true });
605
+ const existing = await fsPromises.readdir(params.targetDir);
606
606
  await Promise.all(existing.filter((entry) => !isExcluded(entry)).map(async (entry) => {
607
607
  const targetPath = path.join(params.targetDir, entry);
608
608
  if ((await lstatIfExists(targetPath))?.isSymbolicLink()) return;
609
- await runLimitedFs(async () => await fs.rm(targetPath, {
609
+ await runLimitedFs(async () => await fsPromises.rm(targetPath, {
610
610
  recursive: true,
611
611
  force: true
612
612
  }));
613
613
  }));
614
- const sourceEntries = await fs.readdir(params.sourceDir);
614
+ const sourceEntries = await fsPromises.readdir(params.sourceDir);
615
615
  for (const entry of sourceEntries) {
616
616
  if (isExcluded(entry)) continue;
617
617
  await copyTreeWithoutSymlinks({
@@ -623,8 +623,8 @@ async function replaceDirectoryContents(params) {
623
623
  }
624
624
  async function stageDirectoryContents(params) {
625
625
  const isExcluded = createExcludeMatcher(params.excludeDirs);
626
- await fs.mkdir(params.targetDir, { recursive: true });
627
- const sourceEntries = await fs.readdir(params.sourceDir);
626
+ await fsPromises.mkdir(params.targetDir, { recursive: true });
627
+ const sourceEntries = await fsPromises.readdir(params.sourceDir);
628
628
  for (const entry of sourceEntries) {
629
629
  if (isExcluded(entry)) continue;
630
630
  await copyTreeWithoutSymlinks({
@@ -1117,7 +1117,7 @@ var OpenShellSandboxBackendImpl = class {
1117
1117
  await this.ensureSandboxExists();
1118
1118
  await this.maybeSeedRemoteWorkspace();
1119
1119
  const target = this.resolveRemoteTarget(remotePath);
1120
- const stats = await fs.lstat(localPath).catch(() => null);
1120
+ const stats = await fsPromises.lstat(localPath).catch(() => null);
1121
1121
  if (!stats) {
1122
1122
  await this.runPinnedRemotePathMutation({ args: [
1123
1123
  "remove",
@@ -1245,7 +1245,7 @@ var OpenShellSandboxBackendImpl = class {
1245
1245
  script: `${ENSURE_OPEN_SHELL_REMOTE_REAL_DIRECTORY_SCRIPT}\nfind "$1" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +`,
1246
1246
  args: [remoteSkillsWorkspaceDir, this.params.remoteWorkspaceDir]
1247
1247
  });
1248
- const stats = await fs.lstat(this.params.createParams.skillsWorkspaceDir).catch(() => null);
1248
+ const stats = await fsPromises.lstat(this.params.createParams.skillsWorkspaceDir).catch(() => null);
1249
1249
  if (!stats?.isDirectory() || stats.isSymbolicLink()) return;
1250
1250
  await this.uploadPathToRemote(this.params.createParams.skillsWorkspaceDir, remoteSkillsWorkspaceDir);
1251
1251
  }
@@ -1296,7 +1296,7 @@ var OpenShellSandboxBackendImpl = class {
1296
1296
  sourceDir: localPath,
1297
1297
  targetDir: stagedRoot
1298
1298
  });
1299
- const stagedEntries = (await fs.readdir(stagedRoot)).toSorted();
1299
+ const stagedEntries = (await fsPromises.readdir(stagedRoot)).toSorted();
1300
1300
  for (const entry of stagedEntries) {
1301
1301
  const result = await runOpenShellCli({
1302
1302
  context: this.params.execContext,
@@ -1342,17 +1342,17 @@ async function removeMaterializedSkillsFromDownloadedWorkspace(tmpDir) {
1342
1342
  let cursor = tmpDir;
1343
1343
  for (const [index, part] of MATERIALIZED_SKILLS_REMOTE_PARTS.entries()) {
1344
1344
  const next = path.join(cursor, part);
1345
- const stats = await fs.lstat(next).catch(() => null);
1345
+ const stats = await fsPromises.lstat(next).catch(() => null);
1346
1346
  if (!stats) return;
1347
1347
  if (index === MATERIALIZED_SKILLS_REMOTE_PARTS.length - 1) {
1348
- await fs.rm(next, {
1348
+ await fsPromises.rm(next, {
1349
1349
  recursive: true,
1350
1350
  force: true
1351
1351
  });
1352
1352
  return;
1353
1353
  }
1354
1354
  if (stats.isSymbolicLink() || !stats.isDirectory()) {
1355
- await fs.rm(next, {
1355
+ await fsPromises.rm(next, {
1356
1356
  recursive: true,
1357
1357
  force: true
1358
1358
  });
@@ -1363,11 +1363,11 @@ async function removeMaterializedSkillsFromDownloadedWorkspace(tmpDir) {
1363
1363
  }
1364
1364
  async function moveMaterializedSkillsShadowAside(params) {
1365
1365
  const shadowPath = path.join(params.workspaceDir, ...MATERIALIZED_SKILLS_REMOTE_PARTS);
1366
- const parentStats = await fs.lstat(path.dirname(shadowPath)).catch(() => null);
1366
+ const parentStats = await fsPromises.lstat(path.dirname(shadowPath)).catch(() => null);
1367
1367
  if (!parentStats?.isDirectory() || parentStats.isSymbolicLink()) return;
1368
- const shadowStats = await fs.lstat(shadowPath).catch(() => null);
1368
+ const shadowStats = await fsPromises.lstat(shadowPath).catch(() => null);
1369
1369
  if (!shadowStats || shadowStats.isSymbolicLink()) return;
1370
- const preserveRoot = await fs.mkdtemp(path.join(path.dirname(params.tmpDir), "openclaw-openshell-preserve-"));
1370
+ const preserveRoot = await fsPromises.mkdtemp(path.join(path.dirname(params.tmpDir), "openclaw-openshell-preserve-"));
1371
1371
  const preservedPath = path.join(preserveRoot, "sandbox-skills");
1372
1372
  await movePathWithCopyFallback({
1373
1373
  from: shadowPath,
@@ -1384,14 +1384,14 @@ async function restoreMaterializedSkillsShadow(params) {
1384
1384
  try {
1385
1385
  const shadowPath = path.join(params.workspaceDir, ...MATERIALIZED_SKILLS_REMOTE_PARTS);
1386
1386
  const parentPath = path.dirname(shadowPath);
1387
- const parentStats = await fs.lstat(parentPath).catch(() => null);
1387
+ const parentStats = await fsPromises.lstat(parentPath).catch(() => null);
1388
1388
  if (parentStats?.isSymbolicLink()) throw new Error(`Refusing to restore sandbox skills through symlink parent: ${parentPath}`);
1389
- if (parentStats && !parentStats.isDirectory()) await fs.rm(parentPath, {
1389
+ if (parentStats && !parentStats.isDirectory()) await fsPromises.rm(parentPath, {
1390
1390
  recursive: true,
1391
1391
  force: true
1392
1392
  });
1393
- await fs.mkdir(parentPath, { recursive: true });
1394
- await fs.rm(shadowPath, {
1393
+ await fsPromises.mkdir(parentPath, { recursive: true });
1394
+ await fsPromises.rm(shadowPath, {
1395
1395
  recursive: true,
1396
1396
  force: true
1397
1397
  });
@@ -1401,7 +1401,7 @@ async function restoreMaterializedSkillsShadow(params) {
1401
1401
  });
1402
1402
  restored = true;
1403
1403
  } finally {
1404
- if (restored) await fs.rm(params.preserved.preserveRoot, {
1404
+ if (restored) await fsPromises.rm(params.preserved.preserveRoot, {
1405
1405
  recursive: true,
1406
1406
  force: true
1407
1407
  });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/openshell-sandbox",
3
- "version": "2026.7.1-beta.1",
3
+ "version": "2026.7.1-beta.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/openshell-sandbox",
9
- "version": "2026.7.1-beta.1",
9
+ "version": "2026.7.1-beta.4",
10
10
  "dependencies": {
11
11
  "zod": "4.4.3"
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/openshell-sandbox",
3
- "version": "2026.7.1-beta.1",
3
+ "version": "2026.7.1-beta.4",
4
4
  "description": "OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,10 +20,10 @@
20
20
  "minHostVersion": ">=2026.5.12-beta.1"
21
21
  },
22
22
  "compat": {
23
- "pluginApi": ">=2026.7.1-beta.1"
23
+ "pluginApi": ">=2026.7.1-beta.4"
24
24
  },
25
25
  "build": {
26
- "openclawVersion": "2026.7.1-beta.1",
26
+ "openclawVersion": "2026.7.1-beta.4",
27
27
  "bundledDist": false
28
28
  },
29
29
  "release": {
@@ -41,7 +41,7 @@
41
41
  "README.md"
42
42
  ],
43
43
  "peerDependencies": {
44
- "openclaw": ">=2026.7.1-beta.1"
44
+ "openclaw": ">=2026.7.1-beta.4"
45
45
  },
46
46
  "peerDependenciesMeta": {
47
47
  "openclaw": {