@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.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 relative3 = filePath.substring(normalizedPath.length);
9287
- if (relative3.startsWith("/")) {
9288
- relative3 = relative3.substring(1);
9355
+ let relative4 = filePath.substring(normalizedPath.length);
9356
+ if (relative4.startsWith("/")) {
9357
+ relative4 = relative4.substring(1);
9289
9358
  }
9290
- if (!relative3) {
9359
+ if (!relative4) {
9291
9360
  const parts = filePath.split("/");
9292
- relative3 = parts[parts.length - 1] || "";
9361
+ relative4 = parts[parts.length - 1] || "";
9293
9362
  }
9294
- if (import_micromatch.default.isMatch(relative3, effectivePattern, {
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 relative3 = k.substring(normalizedPath.length);
9449
- if (relative3.includes("/")) {
9450
- const subdirName = relative3.split("/")[0];
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 relative3 = itemKey.substring(normalizedPath.length);
17483
- if (relative3.includes("/")) {
17484
- const subdirName = relative3.split("/")[0];
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 relative3 = path4.relative(this.cwd, full);
17684
- if (relative3.startsWith("..") || path4.isAbsolute(relative3)) {
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 relative3 = path4.relative(this.cwd, resolved);
18024
- if (relative3.startsWith("..")) continue;
18025
- const normalizedRelative = relative3.split(path4.sep).join("/");
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 relative3 = path4.relative(this.cwd, fp);
18088
- if (relative3.startsWith("..")) continue;
18089
- const normalizedRelative = relative3.split(path4.sep).join("/");
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 relative3 = k.substring(normalizedPath.length);
18373
- if (relative3.includes("/")) {
18374
- const subdirName = relative3.split("/")[0];
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.finally(() => this.creating.delete(name));
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 posix = __toESM(require("path/posix"));
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: posix.join(targetPath, e.name),
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) => posix.join(targetPath, hostPath.slice(hpNorm.length).split(path5.sep).join("/"));
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 = posix.join(sandboxDir, e.name);
26643
+ const fullSandbox = posix2.join(sandboxDir, e.name);
26258
26644
  try {
26259
26645
  const stat4 = await fs3.stat(fullHost);
26260
26646
  result.push({