@axiom-lattice/core 3.0.1 → 3.0.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.d.mts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +415 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +415 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5905,12 +5905,26 @@ var VolumeFilesystem = class {
|
|
|
5905
5905
|
return { error: String(err) };
|
|
5906
5906
|
}
|
|
5907
5907
|
}
|
|
5908
|
+
/** Delete an existing regular file from the mounted volume. */
|
|
5909
|
+
async delete(filePath) {
|
|
5910
|
+
if (!this.client.delete) {
|
|
5911
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
5912
|
+
}
|
|
5913
|
+
try {
|
|
5914
|
+
await this.client.delete(filePath);
|
|
5915
|
+
return { path: filePath, filesUpdate: null };
|
|
5916
|
+
} catch (error) {
|
|
5917
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5918
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5908
5921
|
edit(_filePath, _oldString, _newString, _replaceAll) {
|
|
5909
5922
|
throw new Error("Not supported on volume backend");
|
|
5910
5923
|
}
|
|
5911
5924
|
};
|
|
5912
5925
|
|
|
5913
5926
|
// src/sandbox_lattice/pathUtils.ts
|
|
5927
|
+
import { posix } from "path";
|
|
5914
5928
|
function normalizeExternalSandboxPath(inputPath) {
|
|
5915
5929
|
if (inputPath === "~" || inputPath === "~/") {
|
|
5916
5930
|
return "/";
|
|
@@ -5923,6 +5937,60 @@ function normalizeExternalSandboxPath(inputPath) {
|
|
|
5923
5937
|
}
|
|
5924
5938
|
return `/${inputPath}`;
|
|
5925
5939
|
}
|
|
5940
|
+
function normalizeDeleteSandboxPath(inputPath) {
|
|
5941
|
+
const normalized = normalizeExternalSandboxPath(inputPath);
|
|
5942
|
+
if (normalized.split("/").includes("..")) {
|
|
5943
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
5944
|
+
}
|
|
5945
|
+
return normalized;
|
|
5946
|
+
}
|
|
5947
|
+
function resolveWorkspacePath(workspace, inputPath) {
|
|
5948
|
+
const root = posix.resolve("/", workspace);
|
|
5949
|
+
const normalizedInput = normalizeExternalSandboxPath(inputPath);
|
|
5950
|
+
if (normalizedInput.split("/").includes("..")) {
|
|
5951
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
5952
|
+
}
|
|
5953
|
+
const alreadyInWorkspace = normalizedInput === root || normalizedInput.startsWith(`${root}/`);
|
|
5954
|
+
const resolved = alreadyInWorkspace ? posix.resolve(normalizedInput) : posix.resolve(root, `.${normalizedInput}`);
|
|
5955
|
+
const relative4 = posix.relative(root, resolved);
|
|
5956
|
+
if (relative4 === ".." || relative4.startsWith("../") || posix.isAbsolute(relative4)) {
|
|
5957
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
5958
|
+
}
|
|
5959
|
+
return resolved;
|
|
5960
|
+
}
|
|
5961
|
+
function quotePosixShellArg(value) {
|
|
5962
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
5963
|
+
}
|
|
5964
|
+
function buildRegularFileGuard(filePath, successCommand, containmentRoot) {
|
|
5965
|
+
const quotedPath = quotePosixShellArg(filePath);
|
|
5966
|
+
const commands = [`target=${quotedPath};`];
|
|
5967
|
+
if (containmentRoot !== void 0) {
|
|
5968
|
+
commands.push(
|
|
5969
|
+
`root=${quotePosixShellArg(containmentRoot)};`,
|
|
5970
|
+
`root_real=$(CDPATH= cd -P "$root" 2>/dev/null && pwd -P) || { printf '%s\\n' 'containment root not found' >&2; exit 5; };`,
|
|
5971
|
+
`case "$target" in /*) target_for_dir=$target ;; *) target_for_dir=./$target ;; esac;`,
|
|
5972
|
+
`parent=$(dirname "$target_for_dir") || exit 5;`,
|
|
5973
|
+
"base=${target_for_dir##*/};",
|
|
5974
|
+
`parent_real=$(CDPATH= cd -P "$parent" 2>/dev/null && pwd -P) || { printf '%s\\n' 'file parent not found' >&2; exit 5; };`,
|
|
5975
|
+
`case "$parent_real" in "$root_real"|"$root_real"/*) ;; *) printf '%s\\n' 'path outside containment root' >&2; exit 6 ;; esac;`,
|
|
5976
|
+
`CDPATH= cd -P "$parent_real" 2>/dev/null || exit 5;`,
|
|
5977
|
+
`target=./$base;`
|
|
5978
|
+
);
|
|
5979
|
+
}
|
|
5980
|
+
commands.push(
|
|
5981
|
+
`if [ -L "$target" ]; then printf '%s\\n' 'symlinks are not allowed' >&2; exit 2;`,
|
|
5982
|
+
`elif [ ! -e "$target" ]; then printf '%s\\n' 'file not found' >&2; exit 3;`,
|
|
5983
|
+
`elif [ ! -f "$target" ]; then printf '%s\\n' 'target is not a regular file' >&2; exit 4;`,
|
|
5984
|
+
`else ${successCommand}; fi`
|
|
5985
|
+
);
|
|
5986
|
+
return commands.join(" ");
|
|
5987
|
+
}
|
|
5988
|
+
function buildAssertRegularFileCommand(filePath, containmentRoot) {
|
|
5989
|
+
return buildRegularFileGuard(filePath, ":", containmentRoot);
|
|
5990
|
+
}
|
|
5991
|
+
function buildDeleteRegularFileCommand(filePath, containmentRoot) {
|
|
5992
|
+
return buildRegularFileGuard(filePath, 'rm -- "$target"', containmentRoot);
|
|
5993
|
+
}
|
|
5926
5994
|
|
|
5927
5995
|
// src/sandbox_lattice/utils.ts
|
|
5928
5996
|
import { createHash } from "crypto";
|
|
@@ -5971,7 +6039,8 @@ function stripPrefixClient(client, prefix) {
|
|
|
5971
6039
|
write: (p, c) => client.write(strip(p), c),
|
|
5972
6040
|
list: (p) => client.list(strip(p)),
|
|
5973
6041
|
readRaw: (p) => client.readRaw(strip(p)),
|
|
5974
|
-
writeRaw: (p, d) => client.writeRaw(strip(p), d)
|
|
6042
|
+
writeRaw: (p, d) => client.writeRaw(strip(p), d),
|
|
6043
|
+
...client.delete ? { delete: (p) => client.delete(strip(p)) } : {}
|
|
5975
6044
|
};
|
|
5976
6045
|
}
|
|
5977
6046
|
function computeSandboxName(config) {
|
|
@@ -7431,15 +7500,15 @@ function globSearchFiles(files, pattern, path8 = "/") {
|
|
|
7431
7500
|
const effectivePattern = pattern;
|
|
7432
7501
|
const matches = [];
|
|
7433
7502
|
for (const [filePath, fileData] of Object.entries(filtered)) {
|
|
7434
|
-
let
|
|
7435
|
-
if (
|
|
7436
|
-
|
|
7503
|
+
let relative4 = filePath.substring(normalizedPath.length);
|
|
7504
|
+
if (relative4.startsWith("/")) {
|
|
7505
|
+
relative4 = relative4.substring(1);
|
|
7437
7506
|
}
|
|
7438
|
-
if (!
|
|
7507
|
+
if (!relative4) {
|
|
7439
7508
|
const parts = filePath.split("/");
|
|
7440
|
-
|
|
7509
|
+
relative4 = parts[parts.length - 1] || "";
|
|
7441
7510
|
}
|
|
7442
|
-
if (micromatch.isMatch(
|
|
7511
|
+
if (micromatch.isMatch(relative4, effectivePattern, {
|
|
7443
7512
|
dot: true,
|
|
7444
7513
|
nobrace: false
|
|
7445
7514
|
})) {
|
|
@@ -7593,9 +7662,9 @@ var StateBackend = class {
|
|
|
7593
7662
|
if (!k.startsWith(normalizedPath)) {
|
|
7594
7663
|
continue;
|
|
7595
7664
|
}
|
|
7596
|
-
const
|
|
7597
|
-
if (
|
|
7598
|
-
const subdirName =
|
|
7665
|
+
const relative4 = k.substring(normalizedPath.length);
|
|
7666
|
+
if (relative4.includes("/")) {
|
|
7667
|
+
const subdirName = relative4.split("/")[0];
|
|
7599
7668
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
7600
7669
|
continue;
|
|
7601
7670
|
}
|
|
@@ -7691,6 +7760,17 @@ var StateBackend = class {
|
|
|
7691
7760
|
occurrences
|
|
7692
7761
|
};
|
|
7693
7762
|
}
|
|
7763
|
+
/** Delete an existing file through a LangGraph state update. */
|
|
7764
|
+
delete(filePath) {
|
|
7765
|
+
const files = this.getFiles();
|
|
7766
|
+
if (!files[filePath]) {
|
|
7767
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
7768
|
+
}
|
|
7769
|
+
return {
|
|
7770
|
+
path: filePath,
|
|
7771
|
+
filesUpdate: { [filePath]: null }
|
|
7772
|
+
};
|
|
7773
|
+
}
|
|
7694
7774
|
/**
|
|
7695
7775
|
* Structured search results or error string for invalid input.
|
|
7696
7776
|
*/
|
|
@@ -8078,12 +8158,14 @@ Path conventions:
|
|
|
8078
8158
|
- read_file: read a file from the filesystem
|
|
8079
8159
|
- write_file: write to a file in the filesystem
|
|
8080
8160
|
- edit_file: edit a file in the filesystem
|
|
8161
|
+
- delete_file: permanently and irreversibly delete an existing regular file from the filesystem
|
|
8081
8162
|
- glob: find files matching a pattern (e.g., "/project/**/*.py")
|
|
8082
8163
|
- grep: search for text within files`;
|
|
8083
8164
|
var LS_TOOL_DESCRIPTION = "List files and directories in a directory";
|
|
8084
8165
|
var READ_FILE_TOOL_DESCRIPTION = "Read the contents of a file. For image files (png, jpg, gif, webp, bmp, svg), returns a visual description when the current model supports vision; otherwise returns an error suggesting a vision-capable model. For audio files (webm, wav, mp3, m4a, ogg, flac, aac, wma, opus, amr), transcribes the content using the default STT model; if none is registered, returns an error with registration instructions.";
|
|
8085
8166
|
var WRITE_FILE_TOOL_DESCRIPTION = "Write content to a new file. Returns an error if the file already exists";
|
|
8086
8167
|
var EDIT_FILE_TOOL_DESCRIPTION = "Edit a file by replacing a specific string with a new string";
|
|
8168
|
+
var DELETE_FILE_TOOL_DESCRIPTION = "Permanently and irreversibly delete an existing regular file. Directories and symbolic links are not allowed. If the target is ambiguous, use ask_user_to_clarify before deleting";
|
|
8087
8169
|
var GLOB_TOOL_DESCRIPTION = "Find files matching a glob pattern (e.g., '**/*.py' for all Python files)";
|
|
8088
8170
|
var GREP_TOOL_DESCRIPTION = "Search for a regex pattern in files. Returns matching files and line numbers";
|
|
8089
8171
|
function createLsTool(backend, options) {
|
|
@@ -8292,6 +8374,48 @@ function createEditFileTool(backend, options) {
|
|
|
8292
8374
|
}
|
|
8293
8375
|
);
|
|
8294
8376
|
}
|
|
8377
|
+
function createDeleteFileTool(backend, options) {
|
|
8378
|
+
const { customDescription } = options;
|
|
8379
|
+
return tool38(
|
|
8380
|
+
async (input, config) => {
|
|
8381
|
+
const toolConfig = config;
|
|
8382
|
+
const runConfig = toolConfig.configurable?.runConfig ?? {};
|
|
8383
|
+
const stateAndStore = {
|
|
8384
|
+
state: getCurrentTaskInput(config),
|
|
8385
|
+
store: toolConfig.store,
|
|
8386
|
+
...runConfig
|
|
8387
|
+
};
|
|
8388
|
+
const resolvedBackend = await getBackend(backend, stateAndStore);
|
|
8389
|
+
const { file_path } = input;
|
|
8390
|
+
if (!resolvedBackend.delete) {
|
|
8391
|
+
return "Error: Backend does not support file deletion";
|
|
8392
|
+
}
|
|
8393
|
+
const result = await resolvedBackend.delete(file_path);
|
|
8394
|
+
if (result.error) {
|
|
8395
|
+
return result.error;
|
|
8396
|
+
}
|
|
8397
|
+
const message = new ToolMessage({
|
|
8398
|
+
content: `Successfully deleted '${file_path}'`,
|
|
8399
|
+
tool_call_id: toolConfig.toolCall?.id ?? "",
|
|
8400
|
+
name: "delete_file",
|
|
8401
|
+
metadata: result.metadata
|
|
8402
|
+
});
|
|
8403
|
+
if (result.filesUpdate) {
|
|
8404
|
+
return new Command({
|
|
8405
|
+
update: { files: result.filesUpdate, messages: [message] }
|
|
8406
|
+
});
|
|
8407
|
+
}
|
|
8408
|
+
return message;
|
|
8409
|
+
},
|
|
8410
|
+
{
|
|
8411
|
+
name: "delete_file",
|
|
8412
|
+
description: customDescription || DELETE_FILE_TOOL_DESCRIPTION,
|
|
8413
|
+
schema: z310.object({
|
|
8414
|
+
file_path: z310.string().describe("Absolute path to the file to delete")
|
|
8415
|
+
})
|
|
8416
|
+
}
|
|
8417
|
+
);
|
|
8418
|
+
}
|
|
8295
8419
|
function createGlobTool(backend, options) {
|
|
8296
8420
|
const { customDescription } = options;
|
|
8297
8421
|
return tool38(
|
|
@@ -8383,6 +8507,9 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
8383
8507
|
createEditFileTool(backend, {
|
|
8384
8508
|
customDescription: customToolDescriptions?.edit_file
|
|
8385
8509
|
}),
|
|
8510
|
+
createDeleteFileTool(backend, {
|
|
8511
|
+
customDescription: customToolDescriptions?.delete_file
|
|
8512
|
+
}),
|
|
8386
8513
|
createGlobTool(backend, {
|
|
8387
8514
|
customDescription: customToolDescriptions?.glob
|
|
8388
8515
|
}),
|
|
@@ -10962,6 +11089,19 @@ var SandboxFilesystem = class {
|
|
|
10962
11089
|
return { error: `Error writing file '${filePath}': ${e.message}` };
|
|
10963
11090
|
}
|
|
10964
11091
|
}
|
|
11092
|
+
/** Delete an existing regular file in the sandbox. */
|
|
11093
|
+
async delete(filePath) {
|
|
11094
|
+
if (!this.sandbox.file.deleteFile) {
|
|
11095
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
11096
|
+
}
|
|
11097
|
+
try {
|
|
11098
|
+
await this.sandbox.file.deleteFile(filePath);
|
|
11099
|
+
return { path: filePath, filesUpdate: null };
|
|
11100
|
+
} catch (error) {
|
|
11101
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11102
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
10965
11105
|
async edit(filePath, oldString, newString, replaceAll = false) {
|
|
10966
11106
|
try {
|
|
10967
11107
|
await this.sandbox.file.strReplaceEditor({
|
|
@@ -15651,9 +15791,9 @@ var StoreBackend = class {
|
|
|
15651
15791
|
if (!itemKey.startsWith(normalizedPath)) {
|
|
15652
15792
|
continue;
|
|
15653
15793
|
}
|
|
15654
|
-
const
|
|
15655
|
-
if (
|
|
15656
|
-
const subdirName =
|
|
15794
|
+
const relative4 = itemKey.substring(normalizedPath.length);
|
|
15795
|
+
if (relative4.includes("/")) {
|
|
15796
|
+
const subdirName = relative4.split("/")[0];
|
|
15657
15797
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
15658
15798
|
continue;
|
|
15659
15799
|
}
|
|
@@ -15760,6 +15900,22 @@ var StoreBackend = class {
|
|
|
15760
15900
|
return { error: `Error: ${e.message}` };
|
|
15761
15901
|
}
|
|
15762
15902
|
}
|
|
15903
|
+
/** Delete an existing persistent file. */
|
|
15904
|
+
async delete(filePath) {
|
|
15905
|
+
try {
|
|
15906
|
+
const store = this.getStore();
|
|
15907
|
+
const namespace = this.getNamespace();
|
|
15908
|
+
const existing = await store.get(namespace, filePath);
|
|
15909
|
+
if (!existing) {
|
|
15910
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
15911
|
+
}
|
|
15912
|
+
await store.delete(namespace, filePath);
|
|
15913
|
+
return { path: filePath, filesUpdate: null };
|
|
15914
|
+
} catch (error) {
|
|
15915
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15916
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
15917
|
+
}
|
|
15918
|
+
}
|
|
15763
15919
|
/**
|
|
15764
15920
|
* Structured search results or error string for invalid input.
|
|
15765
15921
|
*/
|
|
@@ -15852,8 +16008,8 @@ var FilesystemBackend = class {
|
|
|
15852
16008
|
throw new Error("Path traversal not allowed");
|
|
15853
16009
|
}
|
|
15854
16010
|
const full = path4.resolve(this.cwd, vpath.substring(1));
|
|
15855
|
-
const
|
|
15856
|
-
if (
|
|
16011
|
+
const relative4 = path4.relative(this.cwd, full);
|
|
16012
|
+
if (relative4.startsWith("..") || path4.isAbsolute(relative4)) {
|
|
15857
16013
|
throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
|
|
15858
16014
|
}
|
|
15859
16015
|
return full;
|
|
@@ -15867,6 +16023,31 @@ var FilesystemBackend = class {
|
|
|
15867
16023
|
}
|
|
15868
16024
|
return path4.resolve(this.cwd, target);
|
|
15869
16025
|
}
|
|
16026
|
+
async assertVirtualParentContained(resolvedPath) {
|
|
16027
|
+
if (!this.virtualMode) {
|
|
16028
|
+
return;
|
|
16029
|
+
}
|
|
16030
|
+
const [rootPath, parentPath] = await Promise.all([
|
|
16031
|
+
fs2.realpath(this.cwd),
|
|
16032
|
+
fs2.realpath(path4.dirname(resolvedPath))
|
|
16033
|
+
]);
|
|
16034
|
+
const relative4 = path4.relative(rootPath, parentPath);
|
|
16035
|
+
if (relative4 === ".." || relative4.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative4)) {
|
|
16036
|
+
throw new Error(`Path: ${resolvedPath} outside root directory: ${this.cwd}`);
|
|
16037
|
+
}
|
|
16038
|
+
}
|
|
16039
|
+
validateDeleteTarget(filePath, stat4) {
|
|
16040
|
+
if (stat4.isSymbolicLink()) {
|
|
16041
|
+
return `Error: Cannot delete '${filePath}': symlinks are not allowed`;
|
|
16042
|
+
}
|
|
16043
|
+
if (stat4.isDirectory()) {
|
|
16044
|
+
return `Error: Cannot delete '${filePath}': target is a directory`;
|
|
16045
|
+
}
|
|
16046
|
+
if (!stat4.isFile()) {
|
|
16047
|
+
return `Error: Cannot delete '${filePath}': target is not a regular file`;
|
|
16048
|
+
}
|
|
16049
|
+
return void 0;
|
|
16050
|
+
}
|
|
15870
16051
|
/**
|
|
15871
16052
|
* List files and directories in the specified directory (non-recursive).
|
|
15872
16053
|
*
|
|
@@ -16068,6 +16249,50 @@ var FilesystemBackend = class {
|
|
|
16068
16249
|
return { error: `Error writing file '${filePath}': ${e.message}` };
|
|
16069
16250
|
}
|
|
16070
16251
|
}
|
|
16252
|
+
/** Delete an existing regular file without following symbolic links. */
|
|
16253
|
+
async delete(filePath) {
|
|
16254
|
+
let resolvedPath;
|
|
16255
|
+
try {
|
|
16256
|
+
resolvedPath = this.resolvePath(filePath);
|
|
16257
|
+
} catch (error) {
|
|
16258
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16259
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
16260
|
+
}
|
|
16261
|
+
let stat4;
|
|
16262
|
+
try {
|
|
16263
|
+
stat4 = await fs2.lstat(resolvedPath);
|
|
16264
|
+
} catch (error) {
|
|
16265
|
+
if (error.code === "ENOENT") {
|
|
16266
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
16267
|
+
}
|
|
16268
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16269
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
16270
|
+
}
|
|
16271
|
+
const validationError = this.validateDeleteTarget(filePath, stat4);
|
|
16272
|
+
if (validationError) {
|
|
16273
|
+
return { error: validationError };
|
|
16274
|
+
}
|
|
16275
|
+
try {
|
|
16276
|
+
await this.assertVirtualParentContained(resolvedPath);
|
|
16277
|
+
const currentStat = await fs2.lstat(resolvedPath);
|
|
16278
|
+
const currentValidationError = this.validateDeleteTarget(filePath, currentStat);
|
|
16279
|
+
if (currentValidationError) {
|
|
16280
|
+
return { error: currentValidationError };
|
|
16281
|
+
}
|
|
16282
|
+
if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
|
|
16283
|
+
return { error: `Error: Cannot delete '${filePath}': target changed during deletion` };
|
|
16284
|
+
}
|
|
16285
|
+
await this.assertVirtualParentContained(resolvedPath);
|
|
16286
|
+
await fs2.unlink(resolvedPath);
|
|
16287
|
+
return { path: filePath, filesUpdate: null };
|
|
16288
|
+
} catch (error) {
|
|
16289
|
+
if (error.code === "ENOENT") {
|
|
16290
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
16291
|
+
}
|
|
16292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16293
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
16294
|
+
}
|
|
16295
|
+
}
|
|
16071
16296
|
/**
|
|
16072
16297
|
* Edit a file by replacing string occurrences.
|
|
16073
16298
|
* Returns EditResult. External storage sets filesUpdate=null.
|
|
@@ -16192,9 +16417,9 @@ var FilesystemBackend = class {
|
|
|
16192
16417
|
if (this.virtualMode) {
|
|
16193
16418
|
try {
|
|
16194
16419
|
const resolved = path4.resolve(ftext);
|
|
16195
|
-
const
|
|
16196
|
-
if (
|
|
16197
|
-
const normalizedRelative =
|
|
16420
|
+
const relative4 = path4.relative(this.cwd, resolved);
|
|
16421
|
+
if (relative4.startsWith("..")) continue;
|
|
16422
|
+
const normalizedRelative = relative4.split(path4.sep).join("/");
|
|
16198
16423
|
virtPath = "/" + normalizedRelative;
|
|
16199
16424
|
} catch {
|
|
16200
16425
|
continue;
|
|
@@ -16256,9 +16481,9 @@ var FilesystemBackend = class {
|
|
|
16256
16481
|
let virtPath;
|
|
16257
16482
|
if (this.virtualMode) {
|
|
16258
16483
|
try {
|
|
16259
|
-
const
|
|
16260
|
-
if (
|
|
16261
|
-
const normalizedRelative =
|
|
16484
|
+
const relative4 = path4.relative(this.cwd, fp);
|
|
16485
|
+
if (relative4.startsWith("..")) continue;
|
|
16486
|
+
const normalizedRelative = relative4.split(path4.sep).join("/");
|
|
16262
16487
|
virtPath = "/" + normalizedRelative;
|
|
16263
16488
|
} catch {
|
|
16264
16489
|
continue;
|
|
@@ -16509,6 +16734,14 @@ var CompositeBackend = class {
|
|
|
16509
16734
|
const [backend, strippedKey] = this.getBackendAndKey(filePath);
|
|
16510
16735
|
return await backend.write(strippedKey, content);
|
|
16511
16736
|
}
|
|
16737
|
+
/** Delete a file, routing to the same backend selected for write and edit. */
|
|
16738
|
+
async delete(filePath) {
|
|
16739
|
+
const [backend, strippedKey] = this.getBackendAndKey(filePath);
|
|
16740
|
+
if (!backend.delete) {
|
|
16741
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
16742
|
+
}
|
|
16743
|
+
return await backend.delete(strippedKey);
|
|
16744
|
+
}
|
|
16512
16745
|
/**
|
|
16513
16746
|
* Edit a file, routing to appropriate backend.
|
|
16514
16747
|
*
|
|
@@ -16541,9 +16774,9 @@ var MemoryBackend = class {
|
|
|
16541
16774
|
if (!k.startsWith(normalizedPath)) {
|
|
16542
16775
|
continue;
|
|
16543
16776
|
}
|
|
16544
|
-
const
|
|
16545
|
-
if (
|
|
16546
|
-
const subdirName =
|
|
16777
|
+
const relative4 = k.substring(normalizedPath.length);
|
|
16778
|
+
if (relative4.includes("/")) {
|
|
16779
|
+
const subdirName = relative4.split("/")[0];
|
|
16547
16780
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
16548
16781
|
continue;
|
|
16549
16782
|
}
|
|
@@ -16611,6 +16844,14 @@ var MemoryBackend = class {
|
|
|
16611
16844
|
this.files.set(filePath, newFileData);
|
|
16612
16845
|
return { path: filePath, filesUpdate: null, occurrences };
|
|
16613
16846
|
}
|
|
16847
|
+
/** Delete an existing in-memory file. */
|
|
16848
|
+
delete(filePath) {
|
|
16849
|
+
if (!this.files.has(filePath)) {
|
|
16850
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
16851
|
+
}
|
|
16852
|
+
this.files.delete(filePath);
|
|
16853
|
+
return { path: filePath, filesUpdate: null };
|
|
16854
|
+
}
|
|
16614
16855
|
grepRaw(pattern, path8 = "/", glob = null) {
|
|
16615
16856
|
const files = this.getFiles();
|
|
16616
16857
|
return grepMatchesFromFiles(files, pattern, path8, glob);
|
|
@@ -23005,6 +23246,9 @@ var MicrosandboxRemoteInstance = class {
|
|
|
23005
23246
|
}
|
|
23006
23247
|
return Buffer.from(result.content ?? "");
|
|
23007
23248
|
},
|
|
23249
|
+
deleteFile: async (file) => {
|
|
23250
|
+
await this.client.deleteFile(this.name, normalizeExternalSandboxPath(file));
|
|
23251
|
+
},
|
|
23008
23252
|
deletePath: async (path8) => {
|
|
23009
23253
|
const resolved = normalizeExternalSandboxPath(path8);
|
|
23010
23254
|
await this.client.execCommand({
|
|
@@ -23111,6 +23355,12 @@ var MicrosandboxServiceClient = class {
|
|
|
23111
23355
|
body: { sandboxName, path: path8, content }
|
|
23112
23356
|
});
|
|
23113
23357
|
}
|
|
23358
|
+
async deleteFile(sandboxName, path8) {
|
|
23359
|
+
return this.request("/api/files/delete", {
|
|
23360
|
+
method: "POST",
|
|
23361
|
+
body: { sandboxName, path: path8 }
|
|
23362
|
+
});
|
|
23363
|
+
}
|
|
23114
23364
|
async listPath(sandboxName, path8, recursive) {
|
|
23115
23365
|
return this.request("/api/files/list", {
|
|
23116
23366
|
method: "POST",
|
|
@@ -23172,6 +23422,15 @@ var MicrosandboxServiceClient = class {
|
|
|
23172
23422
|
}
|
|
23173
23423
|
);
|
|
23174
23424
|
}
|
|
23425
|
+
async volumeFsDelete(volumeName, path8) {
|
|
23426
|
+
await this.request(
|
|
23427
|
+
`/api/volumes/${encodeURIComponent(volumeName)}/fs/delete`,
|
|
23428
|
+
{
|
|
23429
|
+
method: "POST",
|
|
23430
|
+
body: { path: path8 }
|
|
23431
|
+
}
|
|
23432
|
+
);
|
|
23433
|
+
}
|
|
23175
23434
|
async volumeFsList(volumeName, path8) {
|
|
23176
23435
|
console.log(`[volumeFsList] volume=${volumeName} path="${path8}" url=POST /api/volumes/${encodeURIComponent(volumeName)}/fs/list`);
|
|
23177
23436
|
const result = await this.request(
|
|
@@ -23303,7 +23562,10 @@ var MicrosandboxRemoteProvider = class {
|
|
|
23303
23562
|
return new MicrosandboxRemoteInstance(name, this.client);
|
|
23304
23563
|
})();
|
|
23305
23564
|
this.creating.set(name, creation);
|
|
23306
|
-
creation.
|
|
23565
|
+
creation.then(
|
|
23566
|
+
() => this.creating.delete(name),
|
|
23567
|
+
() => this.creating.delete(name)
|
|
23568
|
+
);
|
|
23307
23569
|
return creation;
|
|
23308
23570
|
}
|
|
23309
23571
|
async getSandbox(name) {
|
|
@@ -23326,6 +23588,7 @@ var MicrosandboxRemoteProvider = class {
|
|
|
23326
23588
|
return {
|
|
23327
23589
|
read: (path8) => this.client.volumeFsRead(volumeName, path8),
|
|
23328
23590
|
write: (path8, content) => this.client.volumeFsWrite(volumeName, path8, content),
|
|
23591
|
+
delete: (path8) => this.client.volumeFsDelete(volumeName, path8),
|
|
23329
23592
|
list: (path8) => this.client.volumeFsList(volumeName, path8),
|
|
23330
23593
|
readRaw: (path8) => this.client.volumeFsDownload(volumeName, path8),
|
|
23331
23594
|
writeRaw: (path8, data) => this.client.volumeFsUpload(volumeName, path8, data),
|
|
@@ -23503,6 +23766,22 @@ var RemoteSandboxInstance = class {
|
|
|
23503
23766
|
const buffer2 = await result.body.arrayBuffer();
|
|
23504
23767
|
return Buffer.from(buffer2);
|
|
23505
23768
|
},
|
|
23769
|
+
deleteFile: async (file) => {
|
|
23770
|
+
const resolved = this.resolveDeletePath(file);
|
|
23771
|
+
const result = await this.client.shell.execCommand({
|
|
23772
|
+
command: buildDeleteRegularFileCommand(
|
|
23773
|
+
resolved,
|
|
23774
|
+
resolveWorkspacePath(this.workspace, "/")
|
|
23775
|
+
)
|
|
23776
|
+
});
|
|
23777
|
+
if (!result.ok) {
|
|
23778
|
+
throw new Error(`deleteFile failed: ${extractFetcherError(result.error)}`);
|
|
23779
|
+
}
|
|
23780
|
+
const exitCode = result.body.data?.exit_code ?? 0;
|
|
23781
|
+
if (exitCode !== 0) {
|
|
23782
|
+
throw new Error(`deleteFile failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`);
|
|
23783
|
+
}
|
|
23784
|
+
},
|
|
23506
23785
|
deletePath: async (path8) => {
|
|
23507
23786
|
const resolved = this.resolvePath(path8);
|
|
23508
23787
|
const result = await this.client.shell.execCommand({
|
|
@@ -23549,6 +23828,9 @@ var RemoteSandboxInstance = class {
|
|
|
23549
23828
|
}
|
|
23550
23829
|
return `${this.workspace}${file}`;
|
|
23551
23830
|
}
|
|
23831
|
+
resolveDeletePath(file) {
|
|
23832
|
+
return resolveWorkspacePath(this.workspace, file);
|
|
23833
|
+
}
|
|
23552
23834
|
async start() {
|
|
23553
23835
|
}
|
|
23554
23836
|
async stop() {
|
|
@@ -23668,6 +23950,19 @@ var RemoteSandboxProvider = class {
|
|
|
23668
23950
|
}
|
|
23669
23951
|
return `${workspace}/${p}`;
|
|
23670
23952
|
};
|
|
23953
|
+
const resolveDelete = (p) => {
|
|
23954
|
+
if (!p || p === "/") {
|
|
23955
|
+
return resolveWorkspacePath(workspace, pathPrefix ?? "/");
|
|
23956
|
+
}
|
|
23957
|
+
if (p === workspace || p.startsWith(`${workspace}/`)) {
|
|
23958
|
+
return resolveWorkspacePath(workspace, p);
|
|
23959
|
+
}
|
|
23960
|
+
if (p.startsWith("/")) {
|
|
23961
|
+
return resolveWorkspacePath(workspace, p);
|
|
23962
|
+
}
|
|
23963
|
+
const prefixed = pathPrefix ? `/${pathPrefix.replace(/^\//, "")}/${p}` : p;
|
|
23964
|
+
return resolveWorkspacePath(workspace, prefixed);
|
|
23965
|
+
};
|
|
23671
23966
|
return {
|
|
23672
23967
|
read: async (path8) => {
|
|
23673
23968
|
const resolved = resolve4(path8);
|
|
@@ -23684,6 +23979,24 @@ var RemoteSandboxProvider = class {
|
|
|
23684
23979
|
throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
|
|
23685
23980
|
}
|
|
23686
23981
|
},
|
|
23982
|
+
delete: async (path8) => {
|
|
23983
|
+
const resolved = resolveDelete(path8);
|
|
23984
|
+
const result = await this.client.shell.execCommand({
|
|
23985
|
+
command: buildDeleteRegularFileCommand(
|
|
23986
|
+
resolved,
|
|
23987
|
+
resolveWorkspacePath(workspace, "/")
|
|
23988
|
+
)
|
|
23989
|
+
});
|
|
23990
|
+
if (!result.ok) {
|
|
23991
|
+
throw new Error(`Volume delete failed: ${extractFetcherError(result.error)}`);
|
|
23992
|
+
}
|
|
23993
|
+
const exitCode = result.body.data?.exit_code ?? 0;
|
|
23994
|
+
if (exitCode !== 0) {
|
|
23995
|
+
throw new Error(
|
|
23996
|
+
`Volume delete failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`
|
|
23997
|
+
);
|
|
23998
|
+
}
|
|
23999
|
+
},
|
|
23687
24000
|
mkdir: async (path8) => {
|
|
23688
24001
|
const resolved = resolve4(path8);
|
|
23689
24002
|
const result = await this.client.shell.execCommand({
|
|
@@ -23802,6 +24115,20 @@ var E2BInstance = class {
|
|
|
23802
24115
|
const data = await this.native.files.read(params.file, { format: "bytes" });
|
|
23803
24116
|
return Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
23804
24117
|
},
|
|
24118
|
+
deleteFile: async (file) => {
|
|
24119
|
+
const deletePath = normalizeDeleteSandboxPath(file);
|
|
24120
|
+
const info = await this.native.files.getInfo(deletePath);
|
|
24121
|
+
if (info.symlinkTarget) {
|
|
24122
|
+
throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
|
|
24123
|
+
}
|
|
24124
|
+
if (info.type === "dir") {
|
|
24125
|
+
throw new Error(`Cannot delete '${file}': target is a directory`);
|
|
24126
|
+
}
|
|
24127
|
+
if (info.type !== "file") {
|
|
24128
|
+
throw new Error(`Cannot delete '${file}': target is not a regular file`);
|
|
24129
|
+
}
|
|
24130
|
+
await this.native.files.remove(deletePath);
|
|
24131
|
+
},
|
|
23805
24132
|
deletePath: async (path8) => {
|
|
23806
24133
|
await this.native.commands.run(`rm -rf "${path8}"`);
|
|
23807
24134
|
},
|
|
@@ -23925,6 +24252,10 @@ function toRelativePath(inputPath) {
|
|
|
23925
24252
|
const normalized = normalizeExternalSandboxPath(inputPath);
|
|
23926
24253
|
return normalized === "/" ? "" : normalized.slice(1);
|
|
23927
24254
|
}
|
|
24255
|
+
function toDeleteRelativePath(inputPath) {
|
|
24256
|
+
const normalized = normalizeDeleteSandboxPath(inputPath);
|
|
24257
|
+
return normalized === "/" ? "" : normalized.slice(1);
|
|
24258
|
+
}
|
|
23928
24259
|
var DaytonaInstance = class {
|
|
23929
24260
|
constructor(name, native) {
|
|
23930
24261
|
this.native = native;
|
|
@@ -23985,6 +24316,18 @@ var DaytonaInstance = class {
|
|
|
23985
24316
|
const buffer2 = await this.native.fs.downloadFile(toRelativePath(params.file));
|
|
23986
24317
|
return Buffer.isBuffer(buffer2) ? buffer2 : Buffer.from(buffer2);
|
|
23987
24318
|
},
|
|
24319
|
+
deleteFile: async (file) => {
|
|
24320
|
+
const relativePath = toDeleteRelativePath(file);
|
|
24321
|
+
const check = await this.native.process.executeCommand(
|
|
24322
|
+
buildAssertRegularFileCommand(relativePath, "."),
|
|
24323
|
+
void 0,
|
|
24324
|
+
void 0
|
|
24325
|
+
);
|
|
24326
|
+
if (check.exitCode !== 0) {
|
|
24327
|
+
throw new Error(check.result || `Cannot delete '${file}': target is not a regular file`);
|
|
24328
|
+
}
|
|
24329
|
+
await this.native.fs.deleteFile(relativePath, false);
|
|
24330
|
+
},
|
|
23988
24331
|
deletePath: async (path8) => {
|
|
23989
24332
|
await this.native.process.executeCommand(`rm -rf "${toRelativePath(path8)}"`, void 0, void 0);
|
|
23990
24333
|
},
|
|
@@ -24248,10 +24591,21 @@ import * as fs4 from "fs/promises";
|
|
|
24248
24591
|
import { execFile } from "child_process";
|
|
24249
24592
|
import * as fs3 from "fs/promises";
|
|
24250
24593
|
import * as path5 from "path";
|
|
24251
|
-
import * as
|
|
24594
|
+
import * as posix2 from "path/posix";
|
|
24252
24595
|
import { promisify } from "util";
|
|
24253
24596
|
var execFileAsync = promisify(execFile);
|
|
24254
24597
|
var isWin = process.platform === "win32";
|
|
24598
|
+
function assertRegularDeleteTarget(file, stat4) {
|
|
24599
|
+
if (stat4.isSymbolicLink()) {
|
|
24600
|
+
throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
|
|
24601
|
+
}
|
|
24602
|
+
if (stat4.isDirectory()) {
|
|
24603
|
+
throw new Error(`Cannot delete '${file}': target is a directory`);
|
|
24604
|
+
}
|
|
24605
|
+
if (!stat4.isFile()) {
|
|
24606
|
+
throw new Error(`Cannot delete '${file}': target is not a regular file`);
|
|
24607
|
+
}
|
|
24608
|
+
}
|
|
24255
24609
|
var LocalSandboxInstance = class {
|
|
24256
24610
|
constructor(name, rootDir) {
|
|
24257
24611
|
this.file = {
|
|
@@ -24275,7 +24629,7 @@ var LocalSandboxInstance = class {
|
|
|
24275
24629
|
const full = path5.join(hp, e.name);
|
|
24276
24630
|
const stat4 = await fs3.stat(full).catch(() => null);
|
|
24277
24631
|
files.push({
|
|
24278
|
-
path:
|
|
24632
|
+
path: posix2.join(targetPath, e.name),
|
|
24279
24633
|
is_dir: e.isDirectory(),
|
|
24280
24634
|
size: stat4?.size ?? 0,
|
|
24281
24635
|
modified_at: stat4?.mtime.toISOString()
|
|
@@ -24292,7 +24646,7 @@ var LocalSandboxInstance = class {
|
|
|
24292
24646
|
);
|
|
24293
24647
|
await this.walkDirFilter(hp, regex, results);
|
|
24294
24648
|
const hpNorm = hp + path5.sep;
|
|
24295
|
-
const toSandboxPath = (hostPath) =>
|
|
24649
|
+
const toSandboxPath = (hostPath) => posix2.join(targetPath, hostPath.slice(hpNorm.length).split(path5.sep).join("/"));
|
|
24296
24650
|
return { files: results.map(toSandboxPath) };
|
|
24297
24651
|
},
|
|
24298
24652
|
searchInFile: async (file, regex) => {
|
|
@@ -24335,6 +24689,38 @@ var LocalSandboxInstance = class {
|
|
|
24335
24689
|
const data = await fs3.readFile(this.hostPath(params.file));
|
|
24336
24690
|
return data;
|
|
24337
24691
|
},
|
|
24692
|
+
deleteFile: async (file) => {
|
|
24693
|
+
const hp = this.hostPath(file);
|
|
24694
|
+
let stat4;
|
|
24695
|
+
try {
|
|
24696
|
+
stat4 = await fs3.lstat(hp);
|
|
24697
|
+
} catch (error) {
|
|
24698
|
+
if (error.code === "ENOENT") {
|
|
24699
|
+
throw new Error(`File '${file}' not found`);
|
|
24700
|
+
}
|
|
24701
|
+
throw error;
|
|
24702
|
+
}
|
|
24703
|
+
assertRegularDeleteTarget(file, stat4);
|
|
24704
|
+
const [rootPath, parentPath] = await Promise.all([
|
|
24705
|
+
fs3.realpath(this.rootDir),
|
|
24706
|
+
fs3.realpath(path5.dirname(hp))
|
|
24707
|
+
]);
|
|
24708
|
+
const relativeParent = path5.relative(rootPath, parentPath);
|
|
24709
|
+
if (relativeParent === ".." || relativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(relativeParent)) {
|
|
24710
|
+
throw new Error(`Path traversal denied: ${file}`);
|
|
24711
|
+
}
|
|
24712
|
+
const currentStat = await fs3.lstat(hp);
|
|
24713
|
+
assertRegularDeleteTarget(file, currentStat);
|
|
24714
|
+
if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
|
|
24715
|
+
throw new Error(`Cannot delete '${file}': target changed during deletion`);
|
|
24716
|
+
}
|
|
24717
|
+
const currentParentPath = await fs3.realpath(path5.dirname(hp));
|
|
24718
|
+
const currentRelativeParent = path5.relative(rootPath, currentParentPath);
|
|
24719
|
+
if (currentRelativeParent === ".." || currentRelativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(currentRelativeParent)) {
|
|
24720
|
+
throw new Error(`Path traversal denied: ${file}`);
|
|
24721
|
+
}
|
|
24722
|
+
await fs3.unlink(hp);
|
|
24723
|
+
},
|
|
24338
24724
|
deletePath: async (targetPath) => {
|
|
24339
24725
|
await fs3.rm(this.hostPath(targetPath), { recursive: true, force: true });
|
|
24340
24726
|
},
|
|
@@ -24411,7 +24797,7 @@ ${errOut}`.trim() : out.trim();
|
|
|
24411
24797
|
}
|
|
24412
24798
|
for (const e of entries) {
|
|
24413
24799
|
const fullHost = path5.join(hostDir, e.name);
|
|
24414
|
-
const fullSandbox =
|
|
24800
|
+
const fullSandbox = posix2.join(sandboxDir, e.name);
|
|
24415
24801
|
try {
|
|
24416
24802
|
const stat4 = await fs3.stat(fullHost);
|
|
24417
24803
|
result.push({
|