@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.
- package/dist/index.js +16 -18
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1347
|
+
const parentStats = await lstatIfExists(path.dirname(shadowPath), "ENOENT");
|
|
1350
1348
|
if (!parentStats?.isDirectory() || parentStats.isSymbolicLink()) return;
|
|
1351
|
-
const shadowStats = await
|
|
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
|
|
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.
|
|
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.
|
|
26
|
+
"pluginApi": ">=2026.10.6"
|
|
27
27
|
},
|
|
28
28
|
"build": {
|
|
29
|
-
"openclawVersion": "2026.10.
|
|
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.
|
|
48
|
+
"openclaw": ">=2026.10.6"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"openclaw": {
|