@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.js
CHANGED
|
@@ -7762,12 +7762,26 @@ var VolumeFilesystem = class {
|
|
|
7762
7762
|
return { error: String(err) };
|
|
7763
7763
|
}
|
|
7764
7764
|
}
|
|
7765
|
+
/** Delete an existing regular file from the mounted volume. */
|
|
7766
|
+
async delete(filePath) {
|
|
7767
|
+
if (!this.client.delete) {
|
|
7768
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
7769
|
+
}
|
|
7770
|
+
try {
|
|
7771
|
+
await this.client.delete(filePath);
|
|
7772
|
+
return { path: filePath, filesUpdate: null };
|
|
7773
|
+
} catch (error) {
|
|
7774
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7775
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7765
7778
|
edit(_filePath, _oldString, _newString, _replaceAll) {
|
|
7766
7779
|
throw new Error("Not supported on volume backend");
|
|
7767
7780
|
}
|
|
7768
7781
|
};
|
|
7769
7782
|
|
|
7770
7783
|
// src/sandbox_lattice/pathUtils.ts
|
|
7784
|
+
var import_node_path = require("path");
|
|
7771
7785
|
function normalizeExternalSandboxPath(inputPath) {
|
|
7772
7786
|
if (inputPath === "~" || inputPath === "~/") {
|
|
7773
7787
|
return "/";
|
|
@@ -7780,6 +7794,60 @@ function normalizeExternalSandboxPath(inputPath) {
|
|
|
7780
7794
|
}
|
|
7781
7795
|
return `/${inputPath}`;
|
|
7782
7796
|
}
|
|
7797
|
+
function normalizeDeleteSandboxPath(inputPath) {
|
|
7798
|
+
const normalized = normalizeExternalSandboxPath(inputPath);
|
|
7799
|
+
if (normalized.split("/").includes("..")) {
|
|
7800
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
7801
|
+
}
|
|
7802
|
+
return normalized;
|
|
7803
|
+
}
|
|
7804
|
+
function resolveWorkspacePath(workspace, inputPath) {
|
|
7805
|
+
const root = import_node_path.posix.resolve("/", workspace);
|
|
7806
|
+
const normalizedInput = normalizeExternalSandboxPath(inputPath);
|
|
7807
|
+
if (normalizedInput.split("/").includes("..")) {
|
|
7808
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
7809
|
+
}
|
|
7810
|
+
const alreadyInWorkspace = normalizedInput === root || normalizedInput.startsWith(`${root}/`);
|
|
7811
|
+
const resolved = alreadyInWorkspace ? import_node_path.posix.resolve(normalizedInput) : import_node_path.posix.resolve(root, `.${normalizedInput}`);
|
|
7812
|
+
const relative4 = import_node_path.posix.relative(root, resolved);
|
|
7813
|
+
if (relative4 === ".." || relative4.startsWith("../") || import_node_path.posix.isAbsolute(relative4)) {
|
|
7814
|
+
throw new Error(`Path traversal denied: ${inputPath}`);
|
|
7815
|
+
}
|
|
7816
|
+
return resolved;
|
|
7817
|
+
}
|
|
7818
|
+
function quotePosixShellArg(value) {
|
|
7819
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
7820
|
+
}
|
|
7821
|
+
function buildRegularFileGuard(filePath, successCommand, containmentRoot) {
|
|
7822
|
+
const quotedPath = quotePosixShellArg(filePath);
|
|
7823
|
+
const commands = [`target=${quotedPath};`];
|
|
7824
|
+
if (containmentRoot !== void 0) {
|
|
7825
|
+
commands.push(
|
|
7826
|
+
`root=${quotePosixShellArg(containmentRoot)};`,
|
|
7827
|
+
`root_real=$(CDPATH= cd -P "$root" 2>/dev/null && pwd -P) || { printf '%s\\n' 'containment root not found' >&2; exit 5; };`,
|
|
7828
|
+
`case "$target" in /*) target_for_dir=$target ;; *) target_for_dir=./$target ;; esac;`,
|
|
7829
|
+
`parent=$(dirname "$target_for_dir") || exit 5;`,
|
|
7830
|
+
"base=${target_for_dir##*/};",
|
|
7831
|
+
`parent_real=$(CDPATH= cd -P "$parent" 2>/dev/null && pwd -P) || { printf '%s\\n' 'file parent not found' >&2; exit 5; };`,
|
|
7832
|
+
`case "$parent_real" in "$root_real"|"$root_real"/*) ;; *) printf '%s\\n' 'path outside containment root' >&2; exit 6 ;; esac;`,
|
|
7833
|
+
`CDPATH= cd -P "$parent_real" 2>/dev/null || exit 5;`,
|
|
7834
|
+
`target=./$base;`
|
|
7835
|
+
);
|
|
7836
|
+
}
|
|
7837
|
+
commands.push(
|
|
7838
|
+
`if [ -L "$target" ]; then printf '%s\\n' 'symlinks are not allowed' >&2; exit 2;`,
|
|
7839
|
+
`elif [ ! -e "$target" ]; then printf '%s\\n' 'file not found' >&2; exit 3;`,
|
|
7840
|
+
`elif [ ! -f "$target" ]; then printf '%s\\n' 'target is not a regular file' >&2; exit 4;`,
|
|
7841
|
+
`else ${successCommand}; fi`
|
|
7842
|
+
);
|
|
7843
|
+
return commands.join(" ");
|
|
7844
|
+
}
|
|
7845
|
+
function buildAssertRegularFileCommand(filePath, containmentRoot) {
|
|
7846
|
+
return buildRegularFileGuard(filePath, ":", containmentRoot);
|
|
7847
|
+
}
|
|
7848
|
+
function buildDeleteRegularFileCommand(filePath, containmentRoot) {
|
|
7849
|
+
return buildRegularFileGuard(filePath, 'rm -- "$target"', containmentRoot);
|
|
7850
|
+
}
|
|
7783
7851
|
|
|
7784
7852
|
// src/sandbox_lattice/utils.ts
|
|
7785
7853
|
var import_node_crypto = require("crypto");
|
|
@@ -7828,7 +7896,8 @@ function stripPrefixClient(client, prefix) {
|
|
|
7828
7896
|
write: (p, c) => client.write(strip(p), c),
|
|
7829
7897
|
list: (p) => client.list(strip(p)),
|
|
7830
7898
|
readRaw: (p) => client.readRaw(strip(p)),
|
|
7831
|
-
writeRaw: (p, d) => client.writeRaw(strip(p), d)
|
|
7899
|
+
writeRaw: (p, d) => client.writeRaw(strip(p), d),
|
|
7900
|
+
...client.delete ? { delete: (p) => client.delete(strip(p)) } : {}
|
|
7832
7901
|
};
|
|
7833
7902
|
}
|
|
7834
7903
|
function computeSandboxName(config) {
|
|
@@ -9283,15 +9352,15 @@ function globSearchFiles(files, pattern, path8 = "/") {
|
|
|
9283
9352
|
const effectivePattern = pattern;
|
|
9284
9353
|
const matches = [];
|
|
9285
9354
|
for (const [filePath, fileData] of Object.entries(filtered)) {
|
|
9286
|
-
let
|
|
9287
|
-
if (
|
|
9288
|
-
|
|
9355
|
+
let relative4 = filePath.substring(normalizedPath.length);
|
|
9356
|
+
if (relative4.startsWith("/")) {
|
|
9357
|
+
relative4 = relative4.substring(1);
|
|
9289
9358
|
}
|
|
9290
|
-
if (!
|
|
9359
|
+
if (!relative4) {
|
|
9291
9360
|
const parts = filePath.split("/");
|
|
9292
|
-
|
|
9361
|
+
relative4 = parts[parts.length - 1] || "";
|
|
9293
9362
|
}
|
|
9294
|
-
if (import_micromatch.default.isMatch(
|
|
9363
|
+
if (import_micromatch.default.isMatch(relative4, effectivePattern, {
|
|
9295
9364
|
dot: true,
|
|
9296
9365
|
nobrace: false
|
|
9297
9366
|
})) {
|
|
@@ -9445,9 +9514,9 @@ var StateBackend = class {
|
|
|
9445
9514
|
if (!k.startsWith(normalizedPath)) {
|
|
9446
9515
|
continue;
|
|
9447
9516
|
}
|
|
9448
|
-
const
|
|
9449
|
-
if (
|
|
9450
|
-
const subdirName =
|
|
9517
|
+
const relative4 = k.substring(normalizedPath.length);
|
|
9518
|
+
if (relative4.includes("/")) {
|
|
9519
|
+
const subdirName = relative4.split("/")[0];
|
|
9451
9520
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
9452
9521
|
continue;
|
|
9453
9522
|
}
|
|
@@ -9543,6 +9612,17 @@ var StateBackend = class {
|
|
|
9543
9612
|
occurrences
|
|
9544
9613
|
};
|
|
9545
9614
|
}
|
|
9615
|
+
/** Delete an existing file through a LangGraph state update. */
|
|
9616
|
+
delete(filePath) {
|
|
9617
|
+
const files = this.getFiles();
|
|
9618
|
+
if (!files[filePath]) {
|
|
9619
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
9620
|
+
}
|
|
9621
|
+
return {
|
|
9622
|
+
path: filePath,
|
|
9623
|
+
filesUpdate: { [filePath]: null }
|
|
9624
|
+
};
|
|
9625
|
+
}
|
|
9546
9626
|
/**
|
|
9547
9627
|
* Structured search results or error string for invalid input.
|
|
9548
9628
|
*/
|
|
@@ -9933,12 +10013,14 @@ Path conventions:
|
|
|
9933
10013
|
- read_file: read a file from the filesystem
|
|
9934
10014
|
- write_file: write to a file in the filesystem
|
|
9935
10015
|
- edit_file: edit a file in the filesystem
|
|
10016
|
+
- delete_file: permanently and irreversibly delete an existing regular file from the filesystem
|
|
9936
10017
|
- glob: find files matching a pattern (e.g., "/project/**/*.py")
|
|
9937
10018
|
- grep: search for text within files`;
|
|
9938
10019
|
var LS_TOOL_DESCRIPTION = "List files and directories in a directory";
|
|
9939
10020
|
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.";
|
|
9940
10021
|
var WRITE_FILE_TOOL_DESCRIPTION = "Write content to a new file. Returns an error if the file already exists";
|
|
9941
10022
|
var EDIT_FILE_TOOL_DESCRIPTION = "Edit a file by replacing a specific string with a new string";
|
|
10023
|
+
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";
|
|
9942
10024
|
var GLOB_TOOL_DESCRIPTION = "Find files matching a glob pattern (e.g., '**/*.py' for all Python files)";
|
|
9943
10025
|
var GREP_TOOL_DESCRIPTION = "Search for a regex pattern in files. Returns matching files and line numbers";
|
|
9944
10026
|
function createLsTool(backend, options) {
|
|
@@ -10147,6 +10229,48 @@ function createEditFileTool(backend, options) {
|
|
|
10147
10229
|
}
|
|
10148
10230
|
);
|
|
10149
10231
|
}
|
|
10232
|
+
function createDeleteFileTool(backend, options) {
|
|
10233
|
+
const { customDescription } = options;
|
|
10234
|
+
return (0, import_langchain40.tool)(
|
|
10235
|
+
async (input, config) => {
|
|
10236
|
+
const toolConfig = config;
|
|
10237
|
+
const runConfig = toolConfig.configurable?.runConfig ?? {};
|
|
10238
|
+
const stateAndStore = {
|
|
10239
|
+
state: (0, import_langgraph4.getCurrentTaskInput)(config),
|
|
10240
|
+
store: toolConfig.store,
|
|
10241
|
+
...runConfig
|
|
10242
|
+
};
|
|
10243
|
+
const resolvedBackend = await getBackend(backend, stateAndStore);
|
|
10244
|
+
const { file_path } = input;
|
|
10245
|
+
if (!resolvedBackend.delete) {
|
|
10246
|
+
return "Error: Backend does not support file deletion";
|
|
10247
|
+
}
|
|
10248
|
+
const result = await resolvedBackend.delete(file_path);
|
|
10249
|
+
if (result.error) {
|
|
10250
|
+
return result.error;
|
|
10251
|
+
}
|
|
10252
|
+
const message = new import_langchain40.ToolMessage({
|
|
10253
|
+
content: `Successfully deleted '${file_path}'`,
|
|
10254
|
+
tool_call_id: toolConfig.toolCall?.id ?? "",
|
|
10255
|
+
name: "delete_file",
|
|
10256
|
+
metadata: result.metadata
|
|
10257
|
+
});
|
|
10258
|
+
if (result.filesUpdate) {
|
|
10259
|
+
return new import_langgraph4.Command({
|
|
10260
|
+
update: { files: result.filesUpdate, messages: [message] }
|
|
10261
|
+
});
|
|
10262
|
+
}
|
|
10263
|
+
return message;
|
|
10264
|
+
},
|
|
10265
|
+
{
|
|
10266
|
+
name: "delete_file",
|
|
10267
|
+
description: customDescription || DELETE_FILE_TOOL_DESCRIPTION,
|
|
10268
|
+
schema: import_v3.z.object({
|
|
10269
|
+
file_path: import_v3.z.string().describe("Absolute path to the file to delete")
|
|
10270
|
+
})
|
|
10271
|
+
}
|
|
10272
|
+
);
|
|
10273
|
+
}
|
|
10150
10274
|
function createGlobTool(backend, options) {
|
|
10151
10275
|
const { customDescription } = options;
|
|
10152
10276
|
return (0, import_langchain40.tool)(
|
|
@@ -10238,6 +10362,9 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
10238
10362
|
createEditFileTool(backend, {
|
|
10239
10363
|
customDescription: customToolDescriptions?.edit_file
|
|
10240
10364
|
}),
|
|
10365
|
+
createDeleteFileTool(backend, {
|
|
10366
|
+
customDescription: customToolDescriptions?.delete_file
|
|
10367
|
+
}),
|
|
10241
10368
|
createGlobTool(backend, {
|
|
10242
10369
|
customDescription: customToolDescriptions?.glob
|
|
10243
10370
|
}),
|
|
@@ -12817,6 +12944,19 @@ var SandboxFilesystem = class {
|
|
|
12817
12944
|
return { error: `Error writing file '${filePath}': ${e.message}` };
|
|
12818
12945
|
}
|
|
12819
12946
|
}
|
|
12947
|
+
/** Delete an existing regular file in the sandbox. */
|
|
12948
|
+
async delete(filePath) {
|
|
12949
|
+
if (!this.sandbox.file.deleteFile) {
|
|
12950
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
12951
|
+
}
|
|
12952
|
+
try {
|
|
12953
|
+
await this.sandbox.file.deleteFile(filePath);
|
|
12954
|
+
return { path: filePath, filesUpdate: null };
|
|
12955
|
+
} catch (error) {
|
|
12956
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12957
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12820
12960
|
async edit(filePath, oldString, newString, replaceAll = false) {
|
|
12821
12961
|
try {
|
|
12822
12962
|
await this.sandbox.file.strReplaceEditor({
|
|
@@ -17479,9 +17619,9 @@ var StoreBackend = class {
|
|
|
17479
17619
|
if (!itemKey.startsWith(normalizedPath)) {
|
|
17480
17620
|
continue;
|
|
17481
17621
|
}
|
|
17482
|
-
const
|
|
17483
|
-
if (
|
|
17484
|
-
const subdirName =
|
|
17622
|
+
const relative4 = itemKey.substring(normalizedPath.length);
|
|
17623
|
+
if (relative4.includes("/")) {
|
|
17624
|
+
const subdirName = relative4.split("/")[0];
|
|
17485
17625
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
17486
17626
|
continue;
|
|
17487
17627
|
}
|
|
@@ -17588,6 +17728,22 @@ var StoreBackend = class {
|
|
|
17588
17728
|
return { error: `Error: ${e.message}` };
|
|
17589
17729
|
}
|
|
17590
17730
|
}
|
|
17731
|
+
/** Delete an existing persistent file. */
|
|
17732
|
+
async delete(filePath) {
|
|
17733
|
+
try {
|
|
17734
|
+
const store = this.getStore();
|
|
17735
|
+
const namespace = this.getNamespace();
|
|
17736
|
+
const existing = await store.get(namespace, filePath);
|
|
17737
|
+
if (!existing) {
|
|
17738
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
17739
|
+
}
|
|
17740
|
+
await store.delete(namespace, filePath);
|
|
17741
|
+
return { path: filePath, filesUpdate: null };
|
|
17742
|
+
} catch (error) {
|
|
17743
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17744
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
17745
|
+
}
|
|
17746
|
+
}
|
|
17591
17747
|
/**
|
|
17592
17748
|
* Structured search results or error string for invalid input.
|
|
17593
17749
|
*/
|
|
@@ -17680,8 +17836,8 @@ var FilesystemBackend = class {
|
|
|
17680
17836
|
throw new Error("Path traversal not allowed");
|
|
17681
17837
|
}
|
|
17682
17838
|
const full = path4.resolve(this.cwd, vpath.substring(1));
|
|
17683
|
-
const
|
|
17684
|
-
if (
|
|
17839
|
+
const relative4 = path4.relative(this.cwd, full);
|
|
17840
|
+
if (relative4.startsWith("..") || path4.isAbsolute(relative4)) {
|
|
17685
17841
|
throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
|
|
17686
17842
|
}
|
|
17687
17843
|
return full;
|
|
@@ -17695,6 +17851,31 @@ var FilesystemBackend = class {
|
|
|
17695
17851
|
}
|
|
17696
17852
|
return path4.resolve(this.cwd, target);
|
|
17697
17853
|
}
|
|
17854
|
+
async assertVirtualParentContained(resolvedPath) {
|
|
17855
|
+
if (!this.virtualMode) {
|
|
17856
|
+
return;
|
|
17857
|
+
}
|
|
17858
|
+
const [rootPath, parentPath] = await Promise.all([
|
|
17859
|
+
fs2.realpath(this.cwd),
|
|
17860
|
+
fs2.realpath(path4.dirname(resolvedPath))
|
|
17861
|
+
]);
|
|
17862
|
+
const relative4 = path4.relative(rootPath, parentPath);
|
|
17863
|
+
if (relative4 === ".." || relative4.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative4)) {
|
|
17864
|
+
throw new Error(`Path: ${resolvedPath} outside root directory: ${this.cwd}`);
|
|
17865
|
+
}
|
|
17866
|
+
}
|
|
17867
|
+
validateDeleteTarget(filePath, stat4) {
|
|
17868
|
+
if (stat4.isSymbolicLink()) {
|
|
17869
|
+
return `Error: Cannot delete '${filePath}': symlinks are not allowed`;
|
|
17870
|
+
}
|
|
17871
|
+
if (stat4.isDirectory()) {
|
|
17872
|
+
return `Error: Cannot delete '${filePath}': target is a directory`;
|
|
17873
|
+
}
|
|
17874
|
+
if (!stat4.isFile()) {
|
|
17875
|
+
return `Error: Cannot delete '${filePath}': target is not a regular file`;
|
|
17876
|
+
}
|
|
17877
|
+
return void 0;
|
|
17878
|
+
}
|
|
17698
17879
|
/**
|
|
17699
17880
|
* List files and directories in the specified directory (non-recursive).
|
|
17700
17881
|
*
|
|
@@ -17896,6 +18077,50 @@ var FilesystemBackend = class {
|
|
|
17896
18077
|
return { error: `Error writing file '${filePath}': ${e.message}` };
|
|
17897
18078
|
}
|
|
17898
18079
|
}
|
|
18080
|
+
/** Delete an existing regular file without following symbolic links. */
|
|
18081
|
+
async delete(filePath) {
|
|
18082
|
+
let resolvedPath;
|
|
18083
|
+
try {
|
|
18084
|
+
resolvedPath = this.resolvePath(filePath);
|
|
18085
|
+
} catch (error) {
|
|
18086
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18087
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
18088
|
+
}
|
|
18089
|
+
let stat4;
|
|
18090
|
+
try {
|
|
18091
|
+
stat4 = await fs2.lstat(resolvedPath);
|
|
18092
|
+
} catch (error) {
|
|
18093
|
+
if (error.code === "ENOENT") {
|
|
18094
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
18095
|
+
}
|
|
18096
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18097
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
18098
|
+
}
|
|
18099
|
+
const validationError = this.validateDeleteTarget(filePath, stat4);
|
|
18100
|
+
if (validationError) {
|
|
18101
|
+
return { error: validationError };
|
|
18102
|
+
}
|
|
18103
|
+
try {
|
|
18104
|
+
await this.assertVirtualParentContained(resolvedPath);
|
|
18105
|
+
const currentStat = await fs2.lstat(resolvedPath);
|
|
18106
|
+
const currentValidationError = this.validateDeleteTarget(filePath, currentStat);
|
|
18107
|
+
if (currentValidationError) {
|
|
18108
|
+
return { error: currentValidationError };
|
|
18109
|
+
}
|
|
18110
|
+
if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
|
|
18111
|
+
return { error: `Error: Cannot delete '${filePath}': target changed during deletion` };
|
|
18112
|
+
}
|
|
18113
|
+
await this.assertVirtualParentContained(resolvedPath);
|
|
18114
|
+
await fs2.unlink(resolvedPath);
|
|
18115
|
+
return { path: filePath, filesUpdate: null };
|
|
18116
|
+
} catch (error) {
|
|
18117
|
+
if (error.code === "ENOENT") {
|
|
18118
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
18119
|
+
}
|
|
18120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18121
|
+
return { error: `Error deleting file '${filePath}': ${message}` };
|
|
18122
|
+
}
|
|
18123
|
+
}
|
|
17899
18124
|
/**
|
|
17900
18125
|
* Edit a file by replacing string occurrences.
|
|
17901
18126
|
* Returns EditResult. External storage sets filesUpdate=null.
|
|
@@ -18020,9 +18245,9 @@ var FilesystemBackend = class {
|
|
|
18020
18245
|
if (this.virtualMode) {
|
|
18021
18246
|
try {
|
|
18022
18247
|
const resolved = path4.resolve(ftext);
|
|
18023
|
-
const
|
|
18024
|
-
if (
|
|
18025
|
-
const normalizedRelative =
|
|
18248
|
+
const relative4 = path4.relative(this.cwd, resolved);
|
|
18249
|
+
if (relative4.startsWith("..")) continue;
|
|
18250
|
+
const normalizedRelative = relative4.split(path4.sep).join("/");
|
|
18026
18251
|
virtPath = "/" + normalizedRelative;
|
|
18027
18252
|
} catch {
|
|
18028
18253
|
continue;
|
|
@@ -18084,9 +18309,9 @@ var FilesystemBackend = class {
|
|
|
18084
18309
|
let virtPath;
|
|
18085
18310
|
if (this.virtualMode) {
|
|
18086
18311
|
try {
|
|
18087
|
-
const
|
|
18088
|
-
if (
|
|
18089
|
-
const normalizedRelative =
|
|
18312
|
+
const relative4 = path4.relative(this.cwd, fp);
|
|
18313
|
+
if (relative4.startsWith("..")) continue;
|
|
18314
|
+
const normalizedRelative = relative4.split(path4.sep).join("/");
|
|
18090
18315
|
virtPath = "/" + normalizedRelative;
|
|
18091
18316
|
} catch {
|
|
18092
18317
|
continue;
|
|
@@ -18337,6 +18562,14 @@ var CompositeBackend = class {
|
|
|
18337
18562
|
const [backend, strippedKey] = this.getBackendAndKey(filePath);
|
|
18338
18563
|
return await backend.write(strippedKey, content);
|
|
18339
18564
|
}
|
|
18565
|
+
/** Delete a file, routing to the same backend selected for write and edit. */
|
|
18566
|
+
async delete(filePath) {
|
|
18567
|
+
const [backend, strippedKey] = this.getBackendAndKey(filePath);
|
|
18568
|
+
if (!backend.delete) {
|
|
18569
|
+
return { error: "Error: Backend does not support file deletion" };
|
|
18570
|
+
}
|
|
18571
|
+
return await backend.delete(strippedKey);
|
|
18572
|
+
}
|
|
18340
18573
|
/**
|
|
18341
18574
|
* Edit a file, routing to appropriate backend.
|
|
18342
18575
|
*
|
|
@@ -18369,9 +18602,9 @@ var MemoryBackend = class {
|
|
|
18369
18602
|
if (!k.startsWith(normalizedPath)) {
|
|
18370
18603
|
continue;
|
|
18371
18604
|
}
|
|
18372
|
-
const
|
|
18373
|
-
if (
|
|
18374
|
-
const subdirName =
|
|
18605
|
+
const relative4 = k.substring(normalizedPath.length);
|
|
18606
|
+
if (relative4.includes("/")) {
|
|
18607
|
+
const subdirName = relative4.split("/")[0];
|
|
18375
18608
|
subdirs.add(normalizedPath + subdirName + "/");
|
|
18376
18609
|
continue;
|
|
18377
18610
|
}
|
|
@@ -18439,6 +18672,14 @@ var MemoryBackend = class {
|
|
|
18439
18672
|
this.files.set(filePath, newFileData);
|
|
18440
18673
|
return { path: filePath, filesUpdate: null, occurrences };
|
|
18441
18674
|
}
|
|
18675
|
+
/** Delete an existing in-memory file. */
|
|
18676
|
+
delete(filePath) {
|
|
18677
|
+
if (!this.files.has(filePath)) {
|
|
18678
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
18679
|
+
}
|
|
18680
|
+
this.files.delete(filePath);
|
|
18681
|
+
return { path: filePath, filesUpdate: null };
|
|
18682
|
+
}
|
|
18442
18683
|
grepRaw(pattern, path8 = "/", glob = null) {
|
|
18443
18684
|
const files = this.getFiles();
|
|
18444
18685
|
return grepMatchesFromFiles(files, pattern, path8, glob);
|
|
@@ -24848,6 +25089,9 @@ var MicrosandboxRemoteInstance = class {
|
|
|
24848
25089
|
}
|
|
24849
25090
|
return Buffer.from(result.content ?? "");
|
|
24850
25091
|
},
|
|
25092
|
+
deleteFile: async (file) => {
|
|
25093
|
+
await this.client.deleteFile(this.name, normalizeExternalSandboxPath(file));
|
|
25094
|
+
},
|
|
24851
25095
|
deletePath: async (path8) => {
|
|
24852
25096
|
const resolved = normalizeExternalSandboxPath(path8);
|
|
24853
25097
|
await this.client.execCommand({
|
|
@@ -24954,6 +25198,12 @@ var MicrosandboxServiceClient = class {
|
|
|
24954
25198
|
body: { sandboxName, path: path8, content }
|
|
24955
25199
|
});
|
|
24956
25200
|
}
|
|
25201
|
+
async deleteFile(sandboxName, path8) {
|
|
25202
|
+
return this.request("/api/files/delete", {
|
|
25203
|
+
method: "POST",
|
|
25204
|
+
body: { sandboxName, path: path8 }
|
|
25205
|
+
});
|
|
25206
|
+
}
|
|
24957
25207
|
async listPath(sandboxName, path8, recursive) {
|
|
24958
25208
|
return this.request("/api/files/list", {
|
|
24959
25209
|
method: "POST",
|
|
@@ -25015,6 +25265,15 @@ var MicrosandboxServiceClient = class {
|
|
|
25015
25265
|
}
|
|
25016
25266
|
);
|
|
25017
25267
|
}
|
|
25268
|
+
async volumeFsDelete(volumeName, path8) {
|
|
25269
|
+
await this.request(
|
|
25270
|
+
`/api/volumes/${encodeURIComponent(volumeName)}/fs/delete`,
|
|
25271
|
+
{
|
|
25272
|
+
method: "POST",
|
|
25273
|
+
body: { path: path8 }
|
|
25274
|
+
}
|
|
25275
|
+
);
|
|
25276
|
+
}
|
|
25018
25277
|
async volumeFsList(volumeName, path8) {
|
|
25019
25278
|
console.log(`[volumeFsList] volume=${volumeName} path="${path8}" url=POST /api/volumes/${encodeURIComponent(volumeName)}/fs/list`);
|
|
25020
25279
|
const result = await this.request(
|
|
@@ -25146,7 +25405,10 @@ var MicrosandboxRemoteProvider = class {
|
|
|
25146
25405
|
return new MicrosandboxRemoteInstance(name, this.client);
|
|
25147
25406
|
})();
|
|
25148
25407
|
this.creating.set(name, creation);
|
|
25149
|
-
creation.
|
|
25408
|
+
creation.then(
|
|
25409
|
+
() => this.creating.delete(name),
|
|
25410
|
+
() => this.creating.delete(name)
|
|
25411
|
+
);
|
|
25150
25412
|
return creation;
|
|
25151
25413
|
}
|
|
25152
25414
|
async getSandbox(name) {
|
|
@@ -25169,6 +25431,7 @@ var MicrosandboxRemoteProvider = class {
|
|
|
25169
25431
|
return {
|
|
25170
25432
|
read: (path8) => this.client.volumeFsRead(volumeName, path8),
|
|
25171
25433
|
write: (path8, content) => this.client.volumeFsWrite(volumeName, path8, content),
|
|
25434
|
+
delete: (path8) => this.client.volumeFsDelete(volumeName, path8),
|
|
25172
25435
|
list: (path8) => this.client.volumeFsList(volumeName, path8),
|
|
25173
25436
|
readRaw: (path8) => this.client.volumeFsDownload(volumeName, path8),
|
|
25174
25437
|
writeRaw: (path8, data) => this.client.volumeFsUpload(volumeName, path8, data),
|
|
@@ -25346,6 +25609,22 @@ var RemoteSandboxInstance = class {
|
|
|
25346
25609
|
const buffer2 = await result.body.arrayBuffer();
|
|
25347
25610
|
return Buffer.from(buffer2);
|
|
25348
25611
|
},
|
|
25612
|
+
deleteFile: async (file) => {
|
|
25613
|
+
const resolved = this.resolveDeletePath(file);
|
|
25614
|
+
const result = await this.client.shell.execCommand({
|
|
25615
|
+
command: buildDeleteRegularFileCommand(
|
|
25616
|
+
resolved,
|
|
25617
|
+
resolveWorkspacePath(this.workspace, "/")
|
|
25618
|
+
)
|
|
25619
|
+
});
|
|
25620
|
+
if (!result.ok) {
|
|
25621
|
+
throw new Error(`deleteFile failed: ${extractFetcherError(result.error)}`);
|
|
25622
|
+
}
|
|
25623
|
+
const exitCode = result.body.data?.exit_code ?? 0;
|
|
25624
|
+
if (exitCode !== 0) {
|
|
25625
|
+
throw new Error(`deleteFile failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`);
|
|
25626
|
+
}
|
|
25627
|
+
},
|
|
25349
25628
|
deletePath: async (path8) => {
|
|
25350
25629
|
const resolved = this.resolvePath(path8);
|
|
25351
25630
|
const result = await this.client.shell.execCommand({
|
|
@@ -25392,6 +25671,9 @@ var RemoteSandboxInstance = class {
|
|
|
25392
25671
|
}
|
|
25393
25672
|
return `${this.workspace}${file}`;
|
|
25394
25673
|
}
|
|
25674
|
+
resolveDeletePath(file) {
|
|
25675
|
+
return resolveWorkspacePath(this.workspace, file);
|
|
25676
|
+
}
|
|
25395
25677
|
async start() {
|
|
25396
25678
|
}
|
|
25397
25679
|
async stop() {
|
|
@@ -25511,6 +25793,19 @@ var RemoteSandboxProvider = class {
|
|
|
25511
25793
|
}
|
|
25512
25794
|
return `${workspace}/${p}`;
|
|
25513
25795
|
};
|
|
25796
|
+
const resolveDelete = (p) => {
|
|
25797
|
+
if (!p || p === "/") {
|
|
25798
|
+
return resolveWorkspacePath(workspace, pathPrefix ?? "/");
|
|
25799
|
+
}
|
|
25800
|
+
if (p === workspace || p.startsWith(`${workspace}/`)) {
|
|
25801
|
+
return resolveWorkspacePath(workspace, p);
|
|
25802
|
+
}
|
|
25803
|
+
if (p.startsWith("/")) {
|
|
25804
|
+
return resolveWorkspacePath(workspace, p);
|
|
25805
|
+
}
|
|
25806
|
+
const prefixed = pathPrefix ? `/${pathPrefix.replace(/^\//, "")}/${p}` : p;
|
|
25807
|
+
return resolveWorkspacePath(workspace, prefixed);
|
|
25808
|
+
};
|
|
25514
25809
|
return {
|
|
25515
25810
|
read: async (path8) => {
|
|
25516
25811
|
const resolved = resolve4(path8);
|
|
@@ -25527,6 +25822,24 @@ var RemoteSandboxProvider = class {
|
|
|
25527
25822
|
throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
|
|
25528
25823
|
}
|
|
25529
25824
|
},
|
|
25825
|
+
delete: async (path8) => {
|
|
25826
|
+
const resolved = resolveDelete(path8);
|
|
25827
|
+
const result = await this.client.shell.execCommand({
|
|
25828
|
+
command: buildDeleteRegularFileCommand(
|
|
25829
|
+
resolved,
|
|
25830
|
+
resolveWorkspacePath(workspace, "/")
|
|
25831
|
+
)
|
|
25832
|
+
});
|
|
25833
|
+
if (!result.ok) {
|
|
25834
|
+
throw new Error(`Volume delete failed: ${extractFetcherError(result.error)}`);
|
|
25835
|
+
}
|
|
25836
|
+
const exitCode = result.body.data?.exit_code ?? 0;
|
|
25837
|
+
if (exitCode !== 0) {
|
|
25838
|
+
throw new Error(
|
|
25839
|
+
`Volume delete failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`
|
|
25840
|
+
);
|
|
25841
|
+
}
|
|
25842
|
+
},
|
|
25530
25843
|
mkdir: async (path8) => {
|
|
25531
25844
|
const resolved = resolve4(path8);
|
|
25532
25845
|
const result = await this.client.shell.execCommand({
|
|
@@ -25645,6 +25958,20 @@ var E2BInstance = class {
|
|
|
25645
25958
|
const data = await this.native.files.read(params.file, { format: "bytes" });
|
|
25646
25959
|
return Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
25647
25960
|
},
|
|
25961
|
+
deleteFile: async (file) => {
|
|
25962
|
+
const deletePath = normalizeDeleteSandboxPath(file);
|
|
25963
|
+
const info = await this.native.files.getInfo(deletePath);
|
|
25964
|
+
if (info.symlinkTarget) {
|
|
25965
|
+
throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
|
|
25966
|
+
}
|
|
25967
|
+
if (info.type === "dir") {
|
|
25968
|
+
throw new Error(`Cannot delete '${file}': target is a directory`);
|
|
25969
|
+
}
|
|
25970
|
+
if (info.type !== "file") {
|
|
25971
|
+
throw new Error(`Cannot delete '${file}': target is not a regular file`);
|
|
25972
|
+
}
|
|
25973
|
+
await this.native.files.remove(deletePath);
|
|
25974
|
+
},
|
|
25648
25975
|
deletePath: async (path8) => {
|
|
25649
25976
|
await this.native.commands.run(`rm -rf "${path8}"`);
|
|
25650
25977
|
},
|
|
@@ -25768,6 +26095,10 @@ function toRelativePath(inputPath) {
|
|
|
25768
26095
|
const normalized = normalizeExternalSandboxPath(inputPath);
|
|
25769
26096
|
return normalized === "/" ? "" : normalized.slice(1);
|
|
25770
26097
|
}
|
|
26098
|
+
function toDeleteRelativePath(inputPath) {
|
|
26099
|
+
const normalized = normalizeDeleteSandboxPath(inputPath);
|
|
26100
|
+
return normalized === "/" ? "" : normalized.slice(1);
|
|
26101
|
+
}
|
|
25771
26102
|
var DaytonaInstance = class {
|
|
25772
26103
|
constructor(name, native) {
|
|
25773
26104
|
this.native = native;
|
|
@@ -25828,6 +26159,18 @@ var DaytonaInstance = class {
|
|
|
25828
26159
|
const buffer2 = await this.native.fs.downloadFile(toRelativePath(params.file));
|
|
25829
26160
|
return Buffer.isBuffer(buffer2) ? buffer2 : Buffer.from(buffer2);
|
|
25830
26161
|
},
|
|
26162
|
+
deleteFile: async (file) => {
|
|
26163
|
+
const relativePath = toDeleteRelativePath(file);
|
|
26164
|
+
const check = await this.native.process.executeCommand(
|
|
26165
|
+
buildAssertRegularFileCommand(relativePath, "."),
|
|
26166
|
+
void 0,
|
|
26167
|
+
void 0
|
|
26168
|
+
);
|
|
26169
|
+
if (check.exitCode !== 0) {
|
|
26170
|
+
throw new Error(check.result || `Cannot delete '${file}': target is not a regular file`);
|
|
26171
|
+
}
|
|
26172
|
+
await this.native.fs.deleteFile(relativePath, false);
|
|
26173
|
+
},
|
|
25831
26174
|
deletePath: async (path8) => {
|
|
25832
26175
|
await this.native.process.executeCommand(`rm -rf "${toRelativePath(path8)}"`, void 0, void 0);
|
|
25833
26176
|
},
|
|
@@ -26091,10 +26434,21 @@ var fs4 = __toESM(require("fs/promises"));
|
|
|
26091
26434
|
var import_node_child_process = require("child_process");
|
|
26092
26435
|
var fs3 = __toESM(require("fs/promises"));
|
|
26093
26436
|
var path5 = __toESM(require("path"));
|
|
26094
|
-
var
|
|
26437
|
+
var posix2 = __toESM(require("path/posix"));
|
|
26095
26438
|
var import_node_util = require("util");
|
|
26096
26439
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
26097
26440
|
var isWin = process.platform === "win32";
|
|
26441
|
+
function assertRegularDeleteTarget(file, stat4) {
|
|
26442
|
+
if (stat4.isSymbolicLink()) {
|
|
26443
|
+
throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
|
|
26444
|
+
}
|
|
26445
|
+
if (stat4.isDirectory()) {
|
|
26446
|
+
throw new Error(`Cannot delete '${file}': target is a directory`);
|
|
26447
|
+
}
|
|
26448
|
+
if (!stat4.isFile()) {
|
|
26449
|
+
throw new Error(`Cannot delete '${file}': target is not a regular file`);
|
|
26450
|
+
}
|
|
26451
|
+
}
|
|
26098
26452
|
var LocalSandboxInstance = class {
|
|
26099
26453
|
constructor(name, rootDir) {
|
|
26100
26454
|
this.file = {
|
|
@@ -26118,7 +26472,7 @@ var LocalSandboxInstance = class {
|
|
|
26118
26472
|
const full = path5.join(hp, e.name);
|
|
26119
26473
|
const stat4 = await fs3.stat(full).catch(() => null);
|
|
26120
26474
|
files.push({
|
|
26121
|
-
path:
|
|
26475
|
+
path: posix2.join(targetPath, e.name),
|
|
26122
26476
|
is_dir: e.isDirectory(),
|
|
26123
26477
|
size: stat4?.size ?? 0,
|
|
26124
26478
|
modified_at: stat4?.mtime.toISOString()
|
|
@@ -26135,7 +26489,7 @@ var LocalSandboxInstance = class {
|
|
|
26135
26489
|
);
|
|
26136
26490
|
await this.walkDirFilter(hp, regex, results);
|
|
26137
26491
|
const hpNorm = hp + path5.sep;
|
|
26138
|
-
const toSandboxPath = (hostPath) =>
|
|
26492
|
+
const toSandboxPath = (hostPath) => posix2.join(targetPath, hostPath.slice(hpNorm.length).split(path5.sep).join("/"));
|
|
26139
26493
|
return { files: results.map(toSandboxPath) };
|
|
26140
26494
|
},
|
|
26141
26495
|
searchInFile: async (file, regex) => {
|
|
@@ -26178,6 +26532,38 @@ var LocalSandboxInstance = class {
|
|
|
26178
26532
|
const data = await fs3.readFile(this.hostPath(params.file));
|
|
26179
26533
|
return data;
|
|
26180
26534
|
},
|
|
26535
|
+
deleteFile: async (file) => {
|
|
26536
|
+
const hp = this.hostPath(file);
|
|
26537
|
+
let stat4;
|
|
26538
|
+
try {
|
|
26539
|
+
stat4 = await fs3.lstat(hp);
|
|
26540
|
+
} catch (error) {
|
|
26541
|
+
if (error.code === "ENOENT") {
|
|
26542
|
+
throw new Error(`File '${file}' not found`);
|
|
26543
|
+
}
|
|
26544
|
+
throw error;
|
|
26545
|
+
}
|
|
26546
|
+
assertRegularDeleteTarget(file, stat4);
|
|
26547
|
+
const [rootPath, parentPath] = await Promise.all([
|
|
26548
|
+
fs3.realpath(this.rootDir),
|
|
26549
|
+
fs3.realpath(path5.dirname(hp))
|
|
26550
|
+
]);
|
|
26551
|
+
const relativeParent = path5.relative(rootPath, parentPath);
|
|
26552
|
+
if (relativeParent === ".." || relativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(relativeParent)) {
|
|
26553
|
+
throw new Error(`Path traversal denied: ${file}`);
|
|
26554
|
+
}
|
|
26555
|
+
const currentStat = await fs3.lstat(hp);
|
|
26556
|
+
assertRegularDeleteTarget(file, currentStat);
|
|
26557
|
+
if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
|
|
26558
|
+
throw new Error(`Cannot delete '${file}': target changed during deletion`);
|
|
26559
|
+
}
|
|
26560
|
+
const currentParentPath = await fs3.realpath(path5.dirname(hp));
|
|
26561
|
+
const currentRelativeParent = path5.relative(rootPath, currentParentPath);
|
|
26562
|
+
if (currentRelativeParent === ".." || currentRelativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(currentRelativeParent)) {
|
|
26563
|
+
throw new Error(`Path traversal denied: ${file}`);
|
|
26564
|
+
}
|
|
26565
|
+
await fs3.unlink(hp);
|
|
26566
|
+
},
|
|
26181
26567
|
deletePath: async (targetPath) => {
|
|
26182
26568
|
await fs3.rm(this.hostPath(targetPath), { recursive: true, force: true });
|
|
26183
26569
|
},
|
|
@@ -26254,7 +26640,7 @@ ${errOut}`.trim() : out.trim();
|
|
|
26254
26640
|
}
|
|
26255
26641
|
for (const e of entries) {
|
|
26256
26642
|
const fullHost = path5.join(hostDir, e.name);
|
|
26257
|
-
const fullSandbox =
|
|
26643
|
+
const fullSandbox = posix2.join(sandboxDir, e.name);
|
|
26258
26644
|
try {
|
|
26259
26645
|
const stat4 = await fs3.stat(fullHost);
|
|
26260
26646
|
result.push({
|