@openclaw/openshell-sandbox 2026.10.5 → 2026.10.6

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.
Files changed (2) hide show
  1. package/dist/index.js +16 -18
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { movePathWithCopyFallback } from "@openclaw/fs-safe/atomic";
8
8
  import { GUEST_FILESYSTEM_PYTHON } from "@openclaw/fs-safe/guest";
9
9
  import { createDeferred, formatPluginConfigIssue, mapPluginConfigIssues } from "openclaw/plugin-sdk/extension-shared";
10
10
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
11
+ import { lstatIfExists } from "openclaw/plugin-sdk/path-existence-runtime";
11
12
  import { FsSafeError, canonicalPathFromExistingAncestor, extractErrorCode } from "openclaw/plugin-sdk/security-runtime";
12
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
13
14
  import { safeParseJson } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -269,7 +270,7 @@ var OpenShellFsBridge = class {
269
270
  async stat(params) {
270
271
  const target = this.resolveTarget(params);
271
272
  const hostPath = target.hostPath;
272
- const stats = await fsPromises.lstat(hostPath).catch(() => null);
273
+ const stats = await lstatIfExists(hostPath, "ENOENT");
273
274
  if (!stats) return null;
274
275
  await assertLocalPathSafety({
275
276
  target,
@@ -463,14 +464,14 @@ function createExcludeMatcher(excludeDirs) {
463
464
  return (name) => excluded.has(normalizeLowercaseStringOrEmpty(name));
464
465
  }
465
466
  const runLimitedFs = pLimit(COPY_TREE_FS_CONCURRENCY);
466
- async function lstatIfExists(targetPath) {
467
+ async function lstatIfExists$1(targetPath) {
467
468
  return await runLimitedFs(fsPromises.lstat, targetPath).catch((error) => {
468
469
  if (extractErrorCode(error) === "ENOENT") return null;
469
470
  throw error;
470
471
  });
471
472
  }
472
473
  async function reconcileMirrorPath(params) {
473
- const targetStats = await lstatIfExists(params.targetPath);
474
+ const targetStats = await lstatIfExists$1(params.targetPath);
474
475
  if (targetStats && !targetStats.isDirectory() && !targetStats.isFile()) return true;
475
476
  const sourceStats = params.sourcePath ? await runLimitedFs(fsPromises.lstat, params.sourcePath) : null;
476
477
  const sourceDir = sourceStats?.isDirectory() ? params.sourcePath : void 0;
@@ -807,11 +808,15 @@ var OpenShellSandboxBackendImpl = class {
807
808
  await this.ensureSandboxExists();
808
809
  const sshSession = await createOpenShellSshSession({ context: this.params.execContext });
809
810
  try {
811
+ let root = this.params.remoteWorkspaceDir;
812
+ try {
813
+ root = this.resolveRemoteTarget(workdir).root;
814
+ } catch {}
810
815
  const result = await runSshSandboxCommand({
811
816
  session: sshSession,
812
817
  remoteCommand: buildRemoteWorkdirValidationCommand({
813
818
  workdir,
814
- root: this.resolveWorkdirValidationRoot(workdir)
819
+ root
815
820
  }),
816
821
  allowFailure: true
817
822
  });
@@ -820,13 +825,6 @@ var OpenShellSandboxBackendImpl = class {
820
825
  await disposeSshSandboxSession(sshSession);
821
826
  }
822
827
  }
823
- resolveWorkdirValidationRoot(workdir) {
824
- try {
825
- return this.resolveRemoteTarget(workdir).root;
826
- } catch {
827
- return this.params.remoteWorkspaceDir;
828
- }
829
- }
830
828
  async validateMirrorWorkdir(workdir) {
831
829
  const normalized = normalizeRemotePath(workdir);
832
830
  const roots = this.workspaceUploadRoots();
@@ -851,7 +849,7 @@ var OpenShellSandboxBackendImpl = class {
851
849
  let local = root.local;
852
850
  for (const part of root.local === skillsWorkspaceDir ? ["", ...parts] : parts) {
853
851
  local = path.join(local, part);
854
- if (!(await fsPromises.lstat(local).catch(() => null))?.isDirectory()) return null;
852
+ if (!(await lstatIfExists(local, "ENOENT"))?.isDirectory()) return null;
855
853
  }
856
854
  return normalized;
857
855
  }
@@ -941,7 +939,7 @@ var OpenShellSandboxBackendImpl = class {
941
939
  await this.ensureSandboxExists();
942
940
  await this.maybeSeedRemoteWorkspace();
943
941
  const target = this.resolveRemoteTarget(remotePath);
944
- const stats = await fsPromises.lstat(localPath).catch(() => null);
942
+ const stats = await lstatIfExists(localPath, "ENOENT");
945
943
  if (!stats || stats.isSymbolicLink()) {
946
944
  await this.removeRemotePath(remotePath, {
947
945
  recursive: true,
@@ -1160,11 +1158,11 @@ var OpenShellSandboxBackendImpl = class {
1160
1158
  async syncSkillsWorkspaceToRemote() {
1161
1159
  if (this.params.createParams.cfg.workspaceAccess !== "rw" || !this.params.createParams.skillsWorkspaceDir) return;
1162
1160
  const remoteSkillsWorkspaceDir = resolveRemoteMaterializedSkillsWorkspaceDir(this.params.remoteWorkspaceDir);
1161
+ const stats = await lstatIfExists(this.params.createParams.skillsWorkspaceDir, "ENOENT");
1163
1162
  await this.runRemoteShellScriptInternal({
1164
1163
  script: `${ENSURE_OPEN_SHELL_REMOTE_REAL_DIRECTORY_SCRIPT}\nfind "$1" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +`,
1165
1164
  args: [remoteSkillsWorkspaceDir, this.params.remoteWorkspaceDir]
1166
1165
  });
1167
- const stats = await fsPromises.lstat(this.params.createParams.skillsWorkspaceDir).catch(() => null);
1168
1166
  if (!stats?.isDirectory() || stats.isSymbolicLink()) return;
1169
1167
  await this.uploadPathToRemote(this.params.createParams.skillsWorkspaceDir, remoteSkillsWorkspaceDir);
1170
1168
  }
@@ -1332,7 +1330,7 @@ async function removeDownloadedWorkspacePath(tmpDir, parts) {
1332
1330
  let cursor = tmpDir;
1333
1331
  for (const [index, part] of parts.entries()) {
1334
1332
  const next = path.join(cursor, part);
1335
- const stats = await fsPromises.lstat(next).catch(() => null);
1333
+ const stats = await lstatIfExists(next, "ENOENT");
1336
1334
  if (!stats) return;
1337
1335
  if (index === parts.length - 1 || stats.isSymbolicLink() || !stats.isDirectory()) {
1338
1336
  await fsPromises.rm(next, {
@@ -1346,9 +1344,9 @@ async function removeDownloadedWorkspacePath(tmpDir, parts) {
1346
1344
  }
1347
1345
  async function moveLocalShadowAside(params) {
1348
1346
  const shadowPath = path.join(params.workspaceDir, ...params.relativeParts);
1349
- const parentStats = await fsPromises.lstat(path.dirname(shadowPath)).catch(() => null);
1347
+ const parentStats = await lstatIfExists(path.dirname(shadowPath), "ENOENT");
1350
1348
  if (!parentStats?.isDirectory() || parentStats.isSymbolicLink()) return;
1351
- const shadowStats = await fsPromises.lstat(shadowPath).catch(() => null);
1349
+ const shadowStats = await lstatIfExists(shadowPath, "ENOENT");
1352
1350
  if (!shadowStats || shadowStats.isSymbolicLink()) return;
1353
1351
  const preserveRoot = await fsPromises.mkdtemp(path.join(path.dirname(params.tmpDir), "openclaw-openshell-preserve-"));
1354
1352
  let preserved;
@@ -1374,7 +1372,7 @@ async function restoreLocalShadow(preserved) {
1374
1372
  const { shadowPath, receipt } = preserved;
1375
1373
  const assertBackup = () => assertPreservedShadowIdentity(receipt);
1376
1374
  const parentPath = path.dirname(shadowPath);
1377
- const parentStats = await fsPromises.lstat(parentPath).catch(() => null);
1375
+ const parentStats = await lstatIfExists(parentPath, "ENOENT");
1378
1376
  if (parentStats?.isSymbolicLink()) throw new Error(`Refusing to restore workspace shadow through symlink parent: ${parentPath}`);
1379
1377
  if (parentStats && !parentStats.isDirectory()) {
1380
1378
  assertBackup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/openshell-sandbox",
3
- "version": "2026.10.5",
3
+ "version": "2026.10.6",
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",
@@ -23,10 +23,10 @@
23
23
  "minHostVersion": ">=2026.5.12-beta.1"
24
24
  },
25
25
  "compat": {
26
- "pluginApi": ">=2026.10.5"
26
+ "pluginApi": ">=2026.10.6"
27
27
  },
28
28
  "build": {
29
- "openclawVersion": "2026.10.5",
29
+ "openclawVersion": "2026.10.6",
30
30
  "bundledDist": false
31
31
  },
32
32
  "release": {
@@ -45,7 +45,7 @@
45
45
  "assets/activity.svg"
46
46
  ],
47
47
  "peerDependencies": {
48
- "openclaw": ">=2026.10.5"
48
+ "openclaw": ">=2026.10.6"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "openclaw": {