@langchain/sandbox-standard-tests 0.1.0 → 1.0.0

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.
@@ -1,3 +1,4 @@
1
+ import { adaptSandboxProtocol } from "deepagents";
1
2
  //#region src/tests/lifecycle.ts
2
3
  /**
3
4
  * Register sandbox lifecycle tests (create, isRunning, close, two-step init).
@@ -51,7 +52,6 @@ function registerLifecycleTests(getShared, config, timeout) {
51
52
  });
52
53
  });
53
54
  }
54
-
55
55
  //#endregion
56
56
  //#region src/tests/command-execution.ts
57
57
  /**
@@ -89,7 +89,42 @@ function registerCommandExecutionTests(getShared, config, timeout) {
89
89
  }, timeout);
90
90
  });
91
91
  }
92
-
92
+ //#endregion
93
+ //#region src/adapter.ts
94
+ /**
95
+ * Adapter to convert v1 SandboxInstance to v2 SandboxInstanceV2.
96
+ */
97
+ /**
98
+ * Adapt a sandbox instance (v1 or v2) to SandboxInstanceV2.
99
+ *
100
+ * This builds on {@link adaptSandboxProtocol} from deepagents, which handles
101
+ * the core protocol adaptation (BackendProtocol → BackendProtocolV2 and
102
+ * sandbox properties execute/id).
103
+ *
104
+ * This function additionally preserves test-specific properties:
105
+ * - `isRunning` (required)
106
+ * - `close` (optional)
107
+ * - `initialize` (optional)
108
+ * - `uploadFiles` (required, though optional in base protocol)
109
+ * - `downloadFiles` (required, though optional in base protocol)
110
+ *
111
+ * @param sandbox - Sandbox instance (v1 or v2)
112
+ * @returns SandboxInstanceV2-compatible sandbox
113
+ */
114
+ function adaptSandboxInstance(sandbox) {
115
+ const adapted = adaptSandboxProtocol(sandbox);
116
+ const sb = sandbox;
117
+ if (typeof sb.close === "function") adapted.close = () => sb.close();
118
+ if (Object.getOwnPropertyDescriptor(Object.getPrototypeOf(sandbox), "isRunning")?.get) Object.defineProperty(adapted, "isRunning", {
119
+ get: () => sb.isRunning,
120
+ enumerable: true
121
+ });
122
+ else if ("isRunning" in sandbox) adapted.isRunning = sb.isRunning;
123
+ if (typeof sb.initialize === "function") adapted.initialize = () => sb.initialize();
124
+ if (typeof sb.uploadFiles === "function") adapted.uploadFiles = (files) => sb.uploadFiles(files);
125
+ if (typeof sb.downloadFiles === "function") adapted.downloadFiles = (paths) => sb.downloadFiles(paths);
126
+ return adapted;
127
+ }
93
128
  //#endregion
94
129
  //#region src/tests/file-operations.ts
95
130
  /**
@@ -127,11 +162,11 @@ function registerFileOperationTests(getShared, config, timeout) {
127
162
  expect(results[0].error).toBe("file_not_found");
128
163
  }, timeout);
129
164
  it("should use inherited read method from BaseSandbox", async () => {
130
- const shared = getShared();
165
+ const shared = adaptSandboxInstance(getShared());
131
166
  const filePath = config.resolvePath("read-test.txt");
132
167
  const encoder = new TextEncoder();
133
168
  await shared.uploadFiles([[filePath, encoder.encode("Read test content")]]);
134
- expect(await shared.read(filePath)).toContain("Read test content");
169
+ expect((await shared.read(filePath)).content).toContain("Read test content");
135
170
  }, timeout);
136
171
  it("should use inherited write method from BaseSandbox", async () => {
137
172
  const shared = getShared();
@@ -166,7 +201,6 @@ function registerFileOperationTests(getShared, config, timeout) {
166
201
  }, timeout);
167
202
  });
168
203
  }
169
-
170
204
  //#endregion
171
205
  //#region src/tests/write.ts
172
206
  /**
@@ -251,7 +285,6 @@ function registerWriteTests(getShared, config, timeout) {
251
285
  }, timeout);
252
286
  });
253
287
  }
254
-
255
288
  //#endregion
256
289
  //#region src/tests/read.ts
257
290
  /**
@@ -262,120 +295,119 @@ function registerReadTests(getShared, config, timeout) {
262
295
  const { describe, it, expect } = config.runner;
263
296
  describe("read", () => {
264
297
  it("should read a file with line numbers", async () => {
265
- const shared = getShared();
298
+ const shared = adaptSandboxInstance(getShared());
266
299
  const filePath = config.resolvePath("rd-basic.txt");
267
300
  await shared.write(filePath, "Line 1\nLine 2\nLine 3");
268
301
  const result = await shared.read(filePath);
269
- expect(result).not.toContain("Error:");
270
- expect(result).toContain("Line 1");
271
- expect(result).toContain("Line 2");
272
- expect(result).toContain("Line 3");
302
+ expect(result.error).toBeUndefined();
303
+ expect(result.content).toContain("Line 1");
304
+ expect(result.content).toContain("Line 2");
305
+ expect(result.content).toContain("Line 3");
273
306
  }, timeout);
274
307
  it("should return error for nonexistent file", async () => {
275
308
  const filePath = config.resolvePath("rd-nonexistent-xyz.txt");
276
- const result = await getShared().read(filePath);
277
- expect(result).toContain("Error:");
278
- expect(result.toLowerCase()).toContain("not found");
309
+ const result = await adaptSandboxInstance(getShared()).read(filePath);
310
+ expect(result.error).toBeDefined();
311
+ expect(result.error.toLowerCase()).toContain("not found");
279
312
  }, timeout);
280
313
  it("should handle reading an empty file", async () => {
281
- const shared = getShared();
314
+ const shared = adaptSandboxInstance(getShared());
282
315
  const filePath = config.resolvePath("rd-empty.txt");
283
316
  await shared.write(filePath, "");
284
- expect((await shared.read(filePath)).toLowerCase()).not.toContain("error:");
317
+ expect((await shared.read(filePath)).error).toBeUndefined();
285
318
  }, timeout);
286
319
  it("should read with offset parameter", async () => {
287
- const shared = getShared();
320
+ const shared = adaptSandboxInstance(getShared());
288
321
  const filePath = config.resolvePath("rd-offset.txt");
289
322
  const content = Array.from({ length: 10 }, (_, i) => `Row_${i + 1}_content`).join("\n");
290
323
  await shared.write(filePath, content);
291
324
  const result = await shared.read(filePath, 5);
292
- expect(result).toContain("Row_6_content");
293
- expect(result).not.toContain("Row_1_content");
325
+ expect(result.content).toContain("Row_6_content");
326
+ expect(result.content).not.toContain("Row_1_content");
294
327
  }, timeout);
295
328
  it("should read with limit parameter", async () => {
296
- const shared = getShared();
329
+ const shared = adaptSandboxInstance(getShared());
297
330
  const filePath = config.resolvePath("rd-limit.txt");
298
331
  const content = Array.from({ length: 100 }, (_, i) => `Row_${i + 1}_content`).join("\n");
299
332
  await shared.write(filePath, content);
300
333
  const result = await shared.read(filePath, 0, 5);
301
- expect(result).toContain("Row_1_content");
302
- expect(result).toContain("Row_5_content");
303
- expect(result).not.toContain("Row_6_content");
334
+ expect(result.content).toContain("Row_1_content");
335
+ expect(result.content).toContain("Row_5_content");
336
+ expect(result.content).not.toContain("Row_6_content");
304
337
  }, timeout);
305
338
  it("should read with both offset and limit", async () => {
306
- const shared = getShared();
339
+ const shared = adaptSandboxInstance(getShared());
307
340
  const filePath = config.resolvePath("rd-offset-limit.txt");
308
341
  const content = Array.from({ length: 20 }, (_, i) => `Row_${i + 1}_content`).join("\n");
309
342
  await shared.write(filePath, content);
310
343
  const result = await shared.read(filePath, 10, 5);
311
- expect(result).toContain("Row_11_content");
312
- expect(result).toContain("Row_15_content");
313
- expect(result).not.toContain("Row_10_content");
314
- expect(result).not.toContain("Row_16_content");
344
+ expect(result.content).toContain("Row_11_content");
345
+ expect(result.content).toContain("Row_15_content");
346
+ expect(result.content).not.toContain("Row_10_content");
347
+ expect(result.content).not.toContain("Row_16_content");
315
348
  }, timeout);
316
349
  it("should read unicode content", async () => {
317
- const shared = getShared();
350
+ const shared = adaptSandboxInstance(getShared());
318
351
  const filePath = config.resolvePath("rd-unicode.txt");
319
352
  await shared.write(filePath, "Hello 👋 世界\nПривет мир\nمرحبا العالم");
320
353
  const result = await shared.read(filePath);
321
- expect(result).not.toContain("Error:");
322
- expect(result).toContain("👋");
323
- expect(result).toContain("世界");
324
- expect(result).toContain("Привет");
354
+ expect(result.error).toBeUndefined();
355
+ expect(result.content).toContain("👋");
356
+ expect(result.content).toContain("世界");
357
+ expect(result.content).toContain("Привет");
325
358
  }, timeout);
326
359
  it("should handle files with very long lines", async () => {
327
- const shared = getShared();
360
+ const shared = adaptSandboxInstance(getShared());
328
361
  const filePath = config.resolvePath("rd-long-lines.txt");
329
362
  const content = `Short line\n${"x".repeat(3e3)}\nAnother short line`;
330
363
  await shared.write(filePath, content);
331
364
  const result = await shared.read(filePath);
332
- expect(result).not.toContain("Error:");
333
- expect(result).toContain("Short line");
365
+ expect(result.error).toBeUndefined();
366
+ expect(result.content).toContain("Short line");
334
367
  }, timeout);
335
368
  it("should return nothing with limit=0", async () => {
336
- const shared = getShared();
369
+ const shared = adaptSandboxInstance(getShared());
337
370
  const filePath = config.resolvePath("rd-zero-limit.txt");
338
371
  await shared.write(filePath, "Line 1\nLine 2\nLine 3");
339
- expect(await shared.read(filePath, 0, 0)).not.toContain("Line 1");
372
+ expect((await shared.read(filePath, 0, 0)).content).not.toContain("Line 1");
340
373
  }, timeout);
341
374
  it("should handle offset beyond file length", async () => {
342
- const shared = getShared();
375
+ const shared = adaptSandboxInstance(getShared());
343
376
  const filePath = config.resolvePath("rd-offset-beyond.txt");
344
377
  await shared.write(filePath, "Line 1\nLine 2\nLine 3");
345
378
  const result = await shared.read(filePath, 100, 10);
346
- expect(result).not.toContain("Line 1");
347
- expect(result).not.toContain("Line 2");
348
- expect(result).not.toContain("Line 3");
379
+ expect(result.content ?? "").not.toContain("Line 1");
380
+ expect(result.content ?? "").not.toContain("Line 2");
381
+ expect(result.content ?? "").not.toContain("Line 3");
349
382
  }, timeout);
350
383
  it("should handle offset exactly at file length", async () => {
351
- const shared = getShared();
384
+ const shared = adaptSandboxInstance(getShared());
352
385
  const filePath = config.resolvePath("rd-offset-exact.txt");
353
386
  const content = Array.from({ length: 5 }, (_, i) => `Line ${i + 1}`).join("\n");
354
387
  await shared.write(filePath, content);
355
388
  const result = await shared.read(filePath, 5, 10);
356
- expect(result).not.toContain("Line 1");
357
- expect(result).not.toContain("Line 5");
389
+ expect(result.content ?? "").not.toContain("Line 1");
390
+ expect(result.content ?? "").not.toContain("Line 5");
358
391
  }, timeout);
359
392
  it("should read a large file in chunks", async () => {
360
- const shared = getShared();
393
+ const shared = adaptSandboxInstance(getShared());
361
394
  const filePath = config.resolvePath("rd-chunked.txt");
362
395
  const content = Array.from({ length: 1e3 }, (_, i) => `Line_${String(i).padStart(4, "0")}_content`).join("\n");
363
396
  await shared.write(filePath, content);
364
397
  const chunk1 = await shared.read(filePath, 0, 100);
365
- expect(chunk1).toContain("Line_0000_content");
366
- expect(chunk1).toContain("Line_0099_content");
367
- expect(chunk1).not.toContain("Line_0100_content");
398
+ expect(chunk1.content).toContain("Line_0000_content");
399
+ expect(chunk1.content).toContain("Line_0099_content");
400
+ expect(chunk1.content).not.toContain("Line_0100_content");
368
401
  const chunk2 = await shared.read(filePath, 500, 100);
369
- expect(chunk2).toContain("Line_0500_content");
370
- expect(chunk2).toContain("Line_0599_content");
371
- expect(chunk2).not.toContain("Line_0499_content");
402
+ expect(chunk2.content).toContain("Line_0500_content");
403
+ expect(chunk2.content).toContain("Line_0599_content");
404
+ expect(chunk2.content).not.toContain("Line_0499_content");
372
405
  const chunk3 = await shared.read(filePath, 900, 100);
373
- expect(chunk3).toContain("Line_0900_content");
374
- expect(chunk3).toContain("Line_0999_content");
406
+ expect(chunk3.content).toContain("Line_0900_content");
407
+ expect(chunk3.content).toContain("Line_0999_content");
375
408
  }, timeout);
376
409
  });
377
410
  }
378
-
379
411
  //#endregion
380
412
  //#region src/tests/edit.ts
381
413
  /**
@@ -387,26 +419,26 @@ function registerEditTests(getShared, config, timeout) {
387
419
  const { describe, it, expect } = config.runner;
388
420
  describe("edit", () => {
389
421
  it("should edit a single occurrence", async () => {
390
- const shared = getShared();
422
+ const shared = adaptSandboxInstance(getShared());
391
423
  const filePath = config.resolvePath("ed-single.txt");
392
424
  await shared.write(filePath, "Hello world\nGoodbye world\nHello again");
393
425
  const result = await shared.edit(filePath, "Goodbye", "Farewell");
394
426
  expect(result.error).toBeUndefined();
395
427
  expect(result.occurrences).toBe(1);
396
428
  const content = await shared.read(filePath);
397
- expect(content).toContain("Farewell world");
398
- expect(content).not.toContain("Goodbye");
429
+ expect(content.content).toContain("Farewell world");
430
+ expect(content.content).not.toContain("Goodbye");
399
431
  }, timeout);
400
432
  it("should fail with multiple occurrences without replaceAll", async () => {
401
- const shared = getShared();
433
+ const shared = adaptSandboxInstance(getShared());
402
434
  const filePath = config.resolvePath("ed-multi-fail.txt");
403
435
  await shared.write(filePath, "apple\nbanana\napple\norange\napple");
404
436
  const result = await shared.edit(filePath, "apple", "pear", false);
405
437
  expect(result.error).toBeDefined();
406
438
  expect(result.error.toLowerCase()).toContain("multiple");
407
439
  const content = await shared.read(filePath);
408
- expect(content).toContain("apple");
409
- expect(content).not.toContain("pear");
440
+ expect(content.content).toContain("apple");
441
+ expect(content.content).not.toContain("pear");
410
442
  }, timeout);
411
443
  it("should replace all occurrences with replaceAll=true", async () => {
412
444
  const shared = getShared();
@@ -434,70 +466,70 @@ function registerEditTests(getShared, config, timeout) {
434
466
  expect(result.error.toLowerCase()).toContain("not found");
435
467
  }, timeout);
436
468
  it("should handle special characters and regex metacharacters", async () => {
437
- const shared = getShared();
469
+ const shared = adaptSandboxInstance(getShared());
438
470
  const filePath = config.resolvePath("ed-special.txt");
439
471
  await shared.write(filePath, "Price: $100.00\nPattern: [a-z]*\nPath: /usr/bin");
440
472
  expect((await shared.edit(filePath, "$100.00", "$200.00")).error).toBeUndefined();
441
473
  expect((await shared.edit(filePath, "[a-z]*", "[0-9]+")).error).toBeUndefined();
442
474
  const content = await shared.read(filePath);
443
- expect(content).toContain("$200.00");
444
- expect(content).toContain("[0-9]+");
475
+ expect(content.content).toContain("$200.00");
476
+ expect(content.content).toContain("[0-9]+");
445
477
  }, timeout);
446
478
  it("should handle multiline string replacement", async () => {
447
- const shared = getShared();
479
+ const shared = adaptSandboxInstance(getShared());
448
480
  const filePath = config.resolvePath("ed-multiline.txt");
449
481
  await shared.write(filePath, "Line 1\nLine 2\nLine 3");
450
482
  const result = await shared.edit(filePath, "Line 1\nLine 2", "Combined");
451
483
  expect(result.error).toBeUndefined();
452
484
  expect(result.occurrences).toBe(1);
453
485
  const content = await shared.read(filePath);
454
- expect(content).toContain("Combined");
455
- expect(content).toContain("Line 3");
456
- expect(content).not.toContain("Line 1");
486
+ expect(content.content).toContain("Combined");
487
+ expect(content.content).toContain("Line 3");
488
+ expect(content.content).not.toContain("Line 1");
457
489
  }, timeout);
458
490
  it("should delete content by replacing with empty string", async () => {
459
- const shared = getShared();
491
+ const shared = adaptSandboxInstance(getShared());
460
492
  const filePath = config.resolvePath("ed-delete.txt");
461
493
  await shared.write(filePath, "Keep this\nDelete this part\nKeep this too");
462
494
  const result = await shared.edit(filePath, "Delete this part\n", "");
463
495
  expect(result.error).toBeUndefined();
464
496
  expect(result.occurrences).toBe(1);
465
497
  const content = await shared.read(filePath);
466
- expect(content).toContain("Keep this");
467
- expect(content).toContain("Keep this too");
468
- expect(content).not.toContain("Delete this part");
498
+ expect(content.content).toContain("Keep this");
499
+ expect(content.content).toContain("Keep this too");
500
+ expect(content.content).not.toContain("Delete this part");
469
501
  }, timeout);
470
502
  it("should handle identical old and new strings", async () => {
471
- const shared = getShared();
503
+ const shared = adaptSandboxInstance(getShared());
472
504
  const filePath = config.resolvePath("ed-identical.txt");
473
505
  await shared.write(filePath, "Same text");
474
506
  const result = await shared.edit(filePath, "Same text", "Same text");
475
507
  expect(result.error).toBeUndefined();
476
508
  expect(result.occurrences).toBe(1);
477
- expect(await shared.read(filePath)).toContain("Same text");
509
+ expect((await shared.read(filePath)).content).toContain("Same text");
478
510
  }, timeout);
479
511
  it("should handle unicode content", async () => {
480
- const shared = getShared();
512
+ const shared = adaptSandboxInstance(getShared());
481
513
  const filePath = config.resolvePath("ed-unicode.txt");
482
514
  await shared.write(filePath, "Hello 👋 world\n世界 is beautiful");
483
515
  const result = await shared.edit(filePath, "👋", "🌍");
484
516
  expect(result.error).toBeUndefined();
485
517
  expect(result.occurrences).toBe(1);
486
518
  const content = await shared.read(filePath);
487
- expect(content).toContain("🌍");
488
- expect(content).not.toContain("👋");
519
+ expect(content.content).toContain("🌍");
520
+ expect(content.content).not.toContain("👋");
489
521
  }, timeout);
490
522
  it("should handle whitespace-only strings", async () => {
491
- const shared = getShared();
523
+ const shared = adaptSandboxInstance(getShared());
492
524
  const filePath = config.resolvePath("ed-whitespace.txt");
493
525
  await shared.write(filePath, "Line1 Line2");
494
526
  const result = await shared.edit(filePath, " ", " ");
495
527
  expect(result.error).toBeUndefined();
496
528
  expect(result.occurrences).toBe(1);
497
- expect(await shared.read(filePath)).toContain("Line1 Line2");
529
+ expect((await shared.read(filePath)).content).toContain("Line1 Line2");
498
530
  }, timeout);
499
531
  it("should handle very long old and new strings", async () => {
500
- const shared = getShared();
532
+ const shared = adaptSandboxInstance(getShared());
501
533
  const filePath = config.resolvePath("ed-long.txt");
502
534
  const oldString = "x".repeat(1e3);
503
535
  const newString = "y".repeat(1e3);
@@ -506,18 +538,18 @@ function registerEditTests(getShared, config, timeout) {
506
538
  expect(result.error).toBeUndefined();
507
539
  expect(result.occurrences).toBe(1);
508
540
  const content = await shared.read(filePath);
509
- expect(content).toContain("y".repeat(100));
510
- expect(content).not.toContain("x".repeat(100));
541
+ expect(content.content).toContain("y".repeat(100));
542
+ expect(content.content).not.toContain("x".repeat(100));
511
543
  }, timeout);
512
544
  it("should preserve line endings correctly", async () => {
513
- const shared = getShared();
545
+ const shared = adaptSandboxInstance(getShared());
514
546
  const filePath = config.resolvePath("ed-line-endings.txt");
515
547
  await shared.write(filePath, "Line 1\nLine 2\nLine 3\n");
516
548
  expect((await shared.edit(filePath, "Line 2", "Modified Line 2")).error).toBeUndefined();
517
549
  const content = await shared.read(filePath);
518
- expect(content).toContain("Line 1");
519
- expect(content).toContain("Modified Line 2");
520
- expect(content).toContain("Line 3");
550
+ expect(content.content).toContain("Line 1");
551
+ expect(content.content).toContain("Modified Line 2");
552
+ expect(content.content).toContain("Line 3");
521
553
  }, timeout);
522
554
  it("should edit a substring within a line", async () => {
523
555
  const shared = getShared();
@@ -530,32 +562,35 @@ function registerEditTests(getShared, config, timeout) {
530
562
  }, timeout);
531
563
  });
532
564
  }
533
-
534
565
  //#endregion
535
566
  //#region src/tests/ls-info.ts
536
567
  /**
537
- * Register lsInfo() tests (absolute paths, files + subdirs, empty dir,
568
+ * Register ls() tests (absolute paths, files + subdirs, empty dir,
538
569
  * nonexistent dir, hidden files, spaces, unicode, large dir, trailing slash,
539
570
  * special characters).
540
571
  */
541
572
  function registerLsInfoTests(getShared, config, timeout) {
542
573
  const { describe, it, expect } = config.runner;
543
- describe("lsInfo", () => {
574
+ describe("ls", () => {
544
575
  it("should return absolute paths", async () => {
545
- const shared = getShared();
576
+ const shared = adaptSandboxInstance(getShared());
546
577
  const baseDir = config.resolvePath("li-absolute");
547
578
  await shared.write(`${baseDir}/file.txt`, "content");
548
- const result = await shared.lsInfo(baseDir);
579
+ const lsResult = await shared.ls(baseDir);
580
+ expect(lsResult.error).toBeUndefined();
581
+ const result = lsResult.files || [];
549
582
  expect(result.length).toBe(1);
550
583
  expect(result[0].path).toBe(`${baseDir}/file.txt`);
551
584
  }, timeout);
552
585
  it("should list files and subdirectories", async () => {
553
- const shared = getShared();
586
+ const shared = adaptSandboxInstance(getShared());
554
587
  const baseDir = config.resolvePath("li-basic");
555
588
  await shared.write(`${baseDir}/file1.txt`, "content1");
556
589
  await shared.write(`${baseDir}/file2.txt`, "content2");
557
590
  await shared.execute(`mkdir -p '${baseDir}/subdir'`);
558
- const result = await shared.lsInfo(baseDir);
591
+ const lsResult = await shared.ls(baseDir);
592
+ expect(lsResult.error).toBeUndefined();
593
+ const result = lsResult.files || [];
559
594
  expect(result.length).toBe(3);
560
595
  const paths = result.map((info) => info.path.replace(/\/$/, ""));
561
596
  expect(paths).toContain(`${baseDir}/file1.txt`);
@@ -565,86 +600,101 @@ function registerLsInfoTests(getShared, config, timeout) {
565
600
  else expect(info.is_dir).toBe(false);
566
601
  }, timeout);
567
602
  it("should return empty list for empty directory", async () => {
568
- const shared = getShared();
603
+ const shared = adaptSandboxInstance(getShared());
569
604
  const emptyDir = config.resolvePath("li-empty-dir");
570
605
  await shared.execute(`mkdir -p '${emptyDir}'`);
571
- expect(await shared.lsInfo(emptyDir)).toEqual([]);
606
+ const lsResult = await shared.ls(emptyDir);
607
+ expect(lsResult.error).toBeUndefined();
608
+ expect(lsResult.files).toEqual([]);
572
609
  }, timeout);
573
610
  it("should return empty list for nonexistent directory", async () => {
574
611
  const nonexistentDir = config.resolvePath("li-does-not-exist-12345");
575
- expect(await getShared().lsInfo(nonexistentDir)).toEqual([]);
612
+ const lsResult = await adaptSandboxInstance(getShared()).ls(nonexistentDir);
613
+ expect(lsResult.error).toBeUndefined();
614
+ expect(lsResult.files).toEqual([]);
576
615
  }, timeout);
577
616
  it("should include hidden files", async () => {
578
- const shared = getShared();
617
+ const shared = adaptSandboxInstance(getShared());
579
618
  const baseDir = config.resolvePath("li-hidden");
580
619
  await shared.write(`${baseDir}/.hidden`, "hidden content");
581
620
  await shared.write(`${baseDir}/visible.txt`, "visible content");
582
- const paths = (await shared.lsInfo(baseDir)).map((info) => info.path);
621
+ const lsResult = await shared.ls(baseDir);
622
+ expect(lsResult.error).toBeUndefined();
623
+ const paths = (lsResult.files || []).map((info) => info.path);
583
624
  expect(paths).toContain(`${baseDir}/.hidden`);
584
625
  expect(paths).toContain(`${baseDir}/visible.txt`);
585
626
  }, timeout);
586
627
  it("should handle directories with spaces in names", async () => {
587
- const shared = getShared();
628
+ const shared = adaptSandboxInstance(getShared());
588
629
  const baseDir = config.resolvePath("li-spaces");
589
630
  await shared.write(`${baseDir}/file with spaces.txt`, "content");
590
631
  await shared.execute(`mkdir -p '${baseDir}/dir with spaces'`);
591
- const paths = (await shared.lsInfo(baseDir)).map((info) => info.path.replace(/\/$/, ""));
632
+ const lsResult = await shared.ls(baseDir);
633
+ expect(lsResult.error).toBeUndefined();
634
+ const paths = (lsResult.files || []).map((info) => info.path.replace(/\/$/, ""));
592
635
  expect(paths).toContain(`${baseDir}/file with spaces.txt`);
593
636
  expect(paths).toContain(`${baseDir}/dir with spaces`);
594
637
  }, timeout);
595
638
  it("should handle unicode filenames", async () => {
596
- const shared = getShared();
639
+ const shared = adaptSandboxInstance(getShared());
597
640
  const baseDir = config.resolvePath("li-unicode");
598
641
  await shared.write(`${baseDir}/\u6D4B\u8BD5\u6587\u4EF6.txt`, "content");
599
642
  await shared.write(`${baseDir}/\u0444\u0430\u0439\u043B.txt`, "content");
600
- expect((await shared.lsInfo(baseDir)).length).toBe(2);
643
+ const lsResult = await shared.ls(baseDir);
644
+ expect(lsResult.error).toBeUndefined();
645
+ expect((lsResult.files || []).length).toBe(2);
601
646
  }, timeout);
602
647
  it("should handle large directories", async () => {
603
- const shared = getShared();
648
+ const shared = adaptSandboxInstance(getShared());
604
649
  const baseDir = config.resolvePath("li-large");
605
650
  await shared.execute(`mkdir -p '${baseDir}' && cd '${baseDir}' && for i in \$(seq 0 49); do echo 'content' > file_\$(printf '%03d' \$i).txt; done`);
606
- const result = await shared.lsInfo(baseDir);
651
+ const lsResult = await shared.ls(baseDir);
652
+ expect(lsResult.error).toBeUndefined();
653
+ const result = lsResult.files || [];
607
654
  expect(result.length).toBe(50);
608
655
  const paths = result.map((info) => info.path);
609
656
  expect(paths).toContain(`${baseDir}/file_000.txt`);
610
657
  expect(paths).toContain(`${baseDir}/file_049.txt`);
611
658
  }, timeout);
612
659
  it("should handle trailing slash in path", async () => {
613
- const shared = getShared();
660
+ const shared = adaptSandboxInstance(getShared());
614
661
  const baseDir = config.resolvePath("li-trailing");
615
662
  await shared.write(`${baseDir}/file.txt`, "content");
616
- expect((await shared.lsInfo(`${baseDir}/`)).length).toBeGreaterThanOrEqual(1);
663
+ const lsResult = await shared.ls(`${baseDir}/`);
664
+ expect(lsResult.error).toBeUndefined();
665
+ expect((lsResult.files || []).length).toBeGreaterThanOrEqual(1);
617
666
  }, timeout);
618
667
  it("should handle special characters in filenames", async () => {
619
- const shared = getShared();
668
+ const shared = adaptSandboxInstance(getShared());
620
669
  const baseDir = config.resolvePath("li-special-chars");
621
670
  await shared.write(`${baseDir}/file(1).txt`, "content");
622
671
  await shared.write(`${baseDir}/file-3.txt`, "content");
623
- const paths = (await shared.lsInfo(baseDir)).map((info) => info.path);
672
+ const lsResult = await shared.ls(baseDir);
673
+ expect(lsResult.error).toBeUndefined();
674
+ const paths = (lsResult.files || []).map((info) => info.path);
624
675
  expect(paths).toContain(`${baseDir}/file(1).txt`);
625
676
  expect(paths).toContain(`${baseDir}/file-3.txt`);
626
677
  }, timeout);
627
678
  });
628
679
  }
629
-
630
680
  //#endregion
631
681
  //#region src/tests/grep-raw.ts
632
682
  /**
633
- * Register grepRaw() tests (basic search, glob filter, no matches,
683
+ * Register grep() tests (basic search, glob filter, no matches,
634
684
  * multi matches, literal matching, unicode, case sensitivity, special chars,
635
685
  * empty dir, nested dirs, line numbers).
636
686
  */
637
687
  function registerGrepRawTests(getShared, config, timeout) {
638
688
  const { describe, it, expect } = config.runner;
639
- describe("grepRaw", () => {
689
+ describe("grep", () => {
640
690
  it("should find basic literal pattern matches", async () => {
641
- const shared = getShared();
691
+ const shared = adaptSandboxInstance(getShared());
642
692
  const baseDir = config.resolvePath("gr-basic");
643
693
  await shared.write(`${baseDir}/file1.txt`, "Hello world\nGoodbye world");
644
694
  await shared.write(`${baseDir}/file2.txt`, "Hello there\nGoodbye friend");
645
- const result = await shared.grepRaw("Hello", baseDir);
646
- expect(Array.isArray(result)).toBe(true);
647
- const matches = result;
695
+ const result = await shared.grep("Hello", baseDir);
696
+ expect(result.error).toBeUndefined();
697
+ const matches = result.matches;
648
698
  expect(matches.length).toBe(2);
649
699
  const paths = matches.map((m) => m.path);
650
700
  expect(paths.some((p) => p.includes("file1.txt"))).toBe(true);
@@ -655,32 +705,32 @@ function registerGrepRawTests(getShared, config, timeout) {
655
705
  }
656
706
  }, timeout);
657
707
  it("should filter files with glob pattern", async () => {
658
- const shared = getShared();
708
+ const shared = adaptSandboxInstance(getShared());
659
709
  const baseDir = config.resolvePath("gr-glob");
660
710
  await shared.write(`${baseDir}/test.txt`, "pattern_match");
661
711
  await shared.write(`${baseDir}/test.py`, "pattern_match");
662
712
  await shared.write(`${baseDir}/test.md`, "pattern_match");
663
- const result = await shared.grepRaw("pattern_match", baseDir, "*.py");
664
- expect(Array.isArray(result)).toBe(true);
665
- const matches = result;
713
+ const result = await shared.grep("pattern_match", baseDir, "*.py");
714
+ expect(result.error).toBeUndefined();
715
+ const matches = result.matches;
666
716
  expect(matches.length).toBe(1);
667
717
  expect(matches[0].path).toContain("test.py");
668
718
  }, timeout);
669
- it("should return empty array when no matches found", async () => {
670
- const shared = getShared();
719
+ it("should return empty matches when no matches found", async () => {
720
+ const shared = adaptSandboxInstance(getShared());
671
721
  const baseDir = config.resolvePath("gr-no-match");
672
722
  await shared.write(`${baseDir}/file.txt`, "Hello world");
673
- const result = await shared.grepRaw("nonexistent_str", baseDir);
674
- expect(Array.isArray(result)).toBe(true);
675
- expect(result.length).toBe(0);
723
+ const result = await shared.grep("nonexistent_str", baseDir);
724
+ expect(result.error).toBeUndefined();
725
+ expect(result.matches.length).toBe(0);
676
726
  }, timeout);
677
727
  it("should find multiple matches in a single file", async () => {
678
- const shared = getShared();
728
+ const shared = adaptSandboxInstance(getShared());
679
729
  const baseDir = config.resolvePath("gr-multi");
680
730
  await shared.write(`${baseDir}/fruits.txt`, "apple\nbanana\napple\norange\napple");
681
- const result = await shared.grepRaw("apple", baseDir);
682
- expect(Array.isArray(result)).toBe(true);
683
- const matches = result;
731
+ const result = await shared.grep("apple", baseDir);
732
+ expect(result.error).toBeUndefined();
733
+ const matches = result.matches;
684
734
  expect(matches.length).toBe(3);
685
735
  expect(matches.map((m) => m.line)).toEqual([
686
736
  1,
@@ -689,99 +739,101 @@ function registerGrepRawTests(getShared, config, timeout) {
689
739
  ]);
690
740
  }, timeout);
691
741
  it("should match literal strings not regex", async () => {
692
- const shared = getShared();
742
+ const shared = adaptSandboxInstance(getShared());
693
743
  const baseDir = config.resolvePath("gr-literal");
694
744
  await shared.write(`${baseDir}/numbers.txt`, "test123\ntest456\nabcdef");
695
- const result = await shared.grepRaw("test123", baseDir);
696
- expect(Array.isArray(result)).toBe(true);
697
- const matches = result;
745
+ const result = await shared.grep("test123", baseDir);
746
+ expect(result.error).toBeUndefined();
747
+ const matches = result.matches;
698
748
  expect(matches.length).toBe(1);
699
749
  expect(matches[0].text).toContain("test123");
700
750
  }, timeout);
701
751
  it("should find unicode patterns", async () => {
702
- const shared = getShared();
752
+ const shared = adaptSandboxInstance(getShared());
703
753
  const baseDir = config.resolvePath("gr-unicode");
704
754
  await shared.write(`${baseDir}/unicode.txt`, "Hello 世界\nПривет мир\n测试 pattern");
705
- const result = await shared.grepRaw("世界", baseDir);
706
- expect(Array.isArray(result)).toBe(true);
707
- const matches = result;
755
+ const result = await shared.grep("世界", baseDir);
756
+ expect(result.error).toBeUndefined();
757
+ const matches = result.matches;
708
758
  expect(matches.length).toBe(1);
709
759
  expect(matches[0].text).toContain("世界");
710
760
  }, timeout);
711
761
  it("should be case-sensitive by default", async () => {
712
- const shared = getShared();
762
+ const shared = adaptSandboxInstance(getShared());
713
763
  const baseDir = config.resolvePath("gr-case");
714
764
  await shared.write(`${baseDir}/case.txt`, "Hello\nhello\nHELLO");
715
- const result = await shared.grepRaw("Hello", baseDir);
716
- expect(Array.isArray(result)).toBe(true);
717
- const matches = result;
765
+ const result = await shared.grep("Hello", baseDir);
766
+ expect(result.error).toBeUndefined();
767
+ const matches = result.matches;
718
768
  expect(matches.length).toBe(1);
719
769
  expect(matches[0].text).toContain("Hello");
720
770
  }, timeout);
721
771
  it("should handle special characters as literals", async () => {
722
- const shared = getShared();
772
+ const shared = adaptSandboxInstance(getShared());
723
773
  const baseDir = config.resolvePath("gr-special");
724
774
  await shared.write(`${baseDir}/special.txt`, "Price: $100\nPath: /usr/bin\nPattern: [a-z]*");
725
- const result1 = await shared.grepRaw("$100", baseDir);
726
- expect(Array.isArray(result1)).toBe(true);
727
- const matches1 = result1;
775
+ const result1 = await shared.grep("$100", baseDir);
776
+ expect(result1.error).toBeUndefined();
777
+ const matches1 = result1.matches;
728
778
  expect(matches1.length).toBe(1);
729
779
  expect(matches1[0].text).toContain("$100");
730
- const result2 = await shared.grepRaw("[a-z]*", baseDir);
731
- expect(Array.isArray(result2)).toBe(true);
732
- const matches2 = result2;
780
+ const result2 = await shared.grep("[a-z]*", baseDir);
781
+ expect(result2.error).toBeUndefined();
782
+ const matches2 = result2.matches;
733
783
  expect(matches2.length).toBe(1);
734
784
  expect(matches2[0].text).toContain("[a-z]*");
735
785
  }, timeout);
736
- it("should return empty array for empty directory", async () => {
737
- const shared = getShared();
786
+ it("should return empty matches for empty directory", async () => {
787
+ const shared = adaptSandboxInstance(getShared());
738
788
  const baseDir = config.resolvePath("gr-empty-dir");
739
789
  await shared.execute(`mkdir -p '${baseDir}'`);
740
- const result = await shared.grepRaw("anything", baseDir);
741
- expect(Array.isArray(result)).toBe(true);
742
- expect(result.length).toBe(0);
790
+ const result = await shared.grep("anything", baseDir);
791
+ expect(result.error).toBeUndefined();
792
+ expect(result.matches.length).toBe(0);
743
793
  }, timeout);
744
794
  it("should search recursively across nested directories", async () => {
745
- const shared = getShared();
795
+ const shared = adaptSandboxInstance(getShared());
746
796
  const baseDir = config.resolvePath("gr-nested");
747
797
  await shared.write(`${baseDir}/root.txt`, "target_nested here");
748
798
  await shared.write(`${baseDir}/sub1/level1.txt`, "target_nested here");
749
799
  await shared.write(`${baseDir}/sub1/sub2/level2.txt`, "target_nested here");
750
- const result = await shared.grepRaw("target_nested", baseDir);
751
- expect(Array.isArray(result)).toBe(true);
752
- expect(result.length).toBe(3);
800
+ const result = await shared.grep("target_nested", baseDir);
801
+ expect(result.error).toBeUndefined();
802
+ const matches = result.matches;
803
+ expect(matches.length).toBe(3);
753
804
  }, timeout);
754
805
  it("should report correct line numbers", async () => {
755
- const shared = getShared();
806
+ const shared = adaptSandboxInstance(getShared());
756
807
  const baseDir = config.resolvePath("gr-line-nums");
757
808
  const content = Array.from({ length: 100 }, (_, i) => `Line ${i + 1}`).join("\n");
758
809
  await shared.write(`${baseDir}/long.txt`, content);
759
- const result = await shared.grepRaw("Line 50", baseDir);
760
- expect(Array.isArray(result)).toBe(true);
761
- const matches = result;
810
+ const result = await shared.grep("Line 50", baseDir);
811
+ expect(result.error).toBeUndefined();
812
+ const matches = result.matches;
762
813
  expect(matches.length).toBe(1);
763
814
  expect(matches[0].line).toBe(50);
764
815
  }, timeout);
765
816
  });
766
817
  }
767
-
768
818
  //#endregion
769
819
  //#region src/tests/glob-info.ts
770
820
  /**
771
- * Register globInfo() tests (wildcard, recursive, no matches, directories,
821
+ * Register glob() tests (wildcard, recursive, no matches, directories,
772
822
  * extension filter, hidden files, character classes, question mark,
773
823
  * multiple extensions, deeply nested).
774
824
  */
775
825
  function registerGlobInfoTests(getShared, config, timeout) {
776
826
  const { describe, it, expect } = config.runner;
777
- describe("globInfo", () => {
827
+ describe("glob", () => {
778
828
  it("should match basic wildcard pattern", async () => {
779
- const shared = getShared();
829
+ const shared = adaptSandboxInstance(getShared());
780
830
  const baseDir = config.resolvePath("gl-basic");
781
831
  await shared.write(`${baseDir}/file1.txt`, "content");
782
832
  await shared.write(`${baseDir}/file2.txt`, "content");
783
833
  await shared.write(`${baseDir}/file3.py`, "content");
784
- const result = await shared.globInfo("*.txt", baseDir);
834
+ const globResult = await shared.glob("*.txt", baseDir);
835
+ expect(globResult.error).toBeUndefined();
836
+ const result = globResult.files || [];
785
837
  expect(result.length).toBe(2);
786
838
  const paths = result.map((info) => info.path);
787
839
  expect(paths).toContain("file1.txt");
@@ -789,29 +841,35 @@ function registerGlobInfoTests(getShared, config, timeout) {
789
841
  expect(paths.every((p) => !p.endsWith(".py"))).toBe(true);
790
842
  }, timeout);
791
843
  it("should match recursive pattern (**)", async () => {
792
- const shared = getShared();
844
+ const shared = adaptSandboxInstance(getShared());
793
845
  const baseDir = config.resolvePath("gl-recursive");
794
846
  await shared.write(`${baseDir}/root.txt`, "content");
795
847
  await shared.write(`${baseDir}/subdir1/nested1.txt`, "content");
796
848
  await shared.write(`${baseDir}/subdir2/nested2.txt`, "content");
797
- const result = await shared.globInfo("**/*.txt", baseDir);
849
+ const globResult = await shared.glob("**/*.txt", baseDir);
850
+ expect(globResult.error).toBeUndefined();
851
+ const result = globResult.files || [];
798
852
  expect(result.length).toBeGreaterThanOrEqual(2);
799
853
  const paths = result.map((info) => info.path);
800
854
  expect(paths.some((p) => p.includes("nested1.txt"))).toBe(true);
801
855
  expect(paths.some((p) => p.includes("nested2.txt"))).toBe(true);
802
856
  }, timeout);
803
857
  it("should return empty array when no matches", async () => {
804
- const shared = getShared();
858
+ const shared = adaptSandboxInstance(getShared());
805
859
  const baseDir = config.resolvePath("gl-no-match");
806
860
  await shared.write(`${baseDir}/file.txt`, "content");
807
- expect(await shared.globInfo("*.py", baseDir)).toEqual([]);
861
+ const globResult = await shared.glob("*.py", baseDir);
862
+ expect(globResult.error).toBeUndefined();
863
+ expect(globResult.files).toEqual([]);
808
864
  }, timeout);
809
865
  it("should include directories in results", async () => {
810
- const shared = getShared();
866
+ const shared = adaptSandboxInstance(getShared());
811
867
  const baseDir = config.resolvePath("gl-dirs");
812
868
  await shared.execute(`mkdir -p '${baseDir}/dir1' '${baseDir}/dir2'`);
813
869
  await shared.write(`${baseDir}/file.txt`, "content");
814
- const result = await shared.globInfo("*", baseDir);
870
+ const globResult = await shared.glob("*", baseDir);
871
+ expect(globResult.error).toBeUndefined();
872
+ const result = globResult.files || [];
815
873
  expect(result.length).toBe(3);
816
874
  const dirCount = result.filter((info) => info.is_dir).length;
817
875
  const fileCount = result.filter((info) => !info.is_dir).length;
@@ -819,33 +877,39 @@ function registerGlobInfoTests(getShared, config, timeout) {
819
877
  expect(fileCount).toBe(1);
820
878
  }, timeout);
821
879
  it("should match specific file extensions", async () => {
822
- const shared = getShared();
880
+ const shared = adaptSandboxInstance(getShared());
823
881
  const baseDir = config.resolvePath("gl-ext");
824
882
  await shared.write(`${baseDir}/test.py`, "content");
825
883
  await shared.write(`${baseDir}/test.txt`, "content");
826
884
  await shared.write(`${baseDir}/test.md`, "content");
827
- const result = await shared.globInfo("*.py", baseDir);
885
+ const globResult = await shared.glob("*.py", baseDir);
886
+ expect(globResult.error).toBeUndefined();
887
+ const result = globResult.files || [];
828
888
  expect(result.length).toBe(1);
829
889
  expect(result[0].path).toContain("test.py");
830
890
  }, timeout);
831
891
  it("should match hidden files explicitly", async () => {
832
- const shared = getShared();
892
+ const shared = adaptSandboxInstance(getShared());
833
893
  const baseDir = config.resolvePath("gl-hidden");
834
894
  await shared.write(`${baseDir}/.hidden1`, "content");
835
895
  await shared.write(`${baseDir}/.hidden2`, "content");
836
896
  await shared.write(`${baseDir}/visible.txt`, "content");
837
- const paths = (await shared.globInfo(".*", baseDir)).map((info) => info.path);
897
+ const globResult = await shared.glob(".*", baseDir);
898
+ expect(globResult.error).toBeUndefined();
899
+ const paths = (globResult.files || []).map((info) => info.path);
838
900
  expect(paths.some((p) => p.includes(".hidden1") || p.includes(".hidden2"))).toBe(true);
839
901
  expect(paths.every((p) => !p.includes("visible"))).toBe(true);
840
902
  }, timeout);
841
903
  it("should match character class patterns", async () => {
842
- const shared = getShared();
904
+ const shared = adaptSandboxInstance(getShared());
843
905
  const baseDir = config.resolvePath("gl-charclass");
844
906
  await shared.write(`${baseDir}/file1.txt`, "content");
845
907
  await shared.write(`${baseDir}/file2.txt`, "content");
846
908
  await shared.write(`${baseDir}/file3.txt`, "content");
847
909
  await shared.write(`${baseDir}/fileA.txt`, "content");
848
- const result = await shared.globInfo("file[1-2].txt", baseDir);
910
+ const globResult = await shared.glob("file[1-2].txt", baseDir);
911
+ expect(globResult.error).toBeUndefined();
912
+ const result = globResult.files || [];
849
913
  expect(result.length).toBe(2);
850
914
  const paths = result.map((info) => info.path);
851
915
  expect(paths).toContain("file1.txt");
@@ -854,39 +918,46 @@ function registerGlobInfoTests(getShared, config, timeout) {
854
918
  expect(paths).not.toContain("fileA.txt");
855
919
  }, timeout);
856
920
  it("should match single character wildcard (?)", async () => {
857
- const shared = getShared();
921
+ const shared = adaptSandboxInstance(getShared());
858
922
  const baseDir = config.resolvePath("gl-question");
859
923
  await shared.write(`${baseDir}/file1.txt`, "content");
860
924
  await shared.write(`${baseDir}/file2.txt`, "content");
861
925
  await shared.write(`${baseDir}/file10.txt`, "content");
862
- const result = await shared.globInfo("file?.txt", baseDir);
926
+ const globResult = await shared.glob("file?.txt", baseDir);
927
+ expect(globResult.error).toBeUndefined();
928
+ const result = globResult.files || [];
863
929
  expect(result.length).toBe(2);
864
930
  expect(result.map((info) => info.path)).not.toContain("file10.txt");
865
931
  }, timeout);
866
932
  it("should match multiple extensions separately", async () => {
867
- const shared = getShared();
933
+ const shared = adaptSandboxInstance(getShared());
868
934
  const baseDir = config.resolvePath("gl-multi-ext");
869
935
  await shared.write(`${baseDir}/file.txt`, "content");
870
936
  await shared.write(`${baseDir}/file.py`, "content");
871
937
  await shared.write(`${baseDir}/file.md`, "content");
872
938
  await shared.write(`${baseDir}/file.js`, "content");
873
- const resultTxt = await shared.globInfo("*.txt", baseDir);
874
- const resultPy = await shared.globInfo("*.py", baseDir);
939
+ const globResultTxt = await shared.glob("*.txt", baseDir);
940
+ expect(globResultTxt.error).toBeUndefined();
941
+ const resultTxt = globResultTxt.files || [];
942
+ const globResultPy = await shared.glob("*.py", baseDir);
943
+ expect(globResultPy.error).toBeUndefined();
944
+ const resultPy = globResultPy.files || [];
875
945
  expect(resultTxt.length).toBe(1);
876
946
  expect(resultPy.length).toBe(1);
877
947
  }, timeout);
878
948
  it("should match deeply nested patterns", async () => {
879
- const shared = getShared();
949
+ const shared = adaptSandboxInstance(getShared());
880
950
  const baseDir = config.resolvePath("gl-deep");
881
951
  await shared.write(`${baseDir}/a/b/c/d/deep.txt`, "content");
882
952
  await shared.write(`${baseDir}/a/b/other.txt`, "content");
883
- const result = await shared.globInfo("**/deep.txt", baseDir);
953
+ const globResult = await shared.glob("**/deep.txt", baseDir);
954
+ expect(globResult.error).toBeUndefined();
955
+ const result = globResult.files || [];
884
956
  expect(result.length).toBeGreaterThanOrEqual(1);
885
957
  expect(result.some((info) => info.path.includes("deep.txt"))).toBe(true);
886
958
  }, timeout);
887
959
  });
888
960
  }
889
-
890
961
  //#endregion
891
962
  //#region src/tests/initial-files.ts
892
963
  /**
@@ -946,7 +1017,7 @@ function registerInitialFilesTests(config, timeout) {
946
1017
  const filePath = config.resolvePath("init-read-test.txt");
947
1018
  const tmp = await withRetry(() => config.createSandbox({ initialFiles: { [filePath]: "Content for read test" } }));
948
1019
  try {
949
- expect(await tmp.read(filePath)).toContain("Content for read test");
1020
+ expect((await adaptSandboxInstance(tmp).read(filePath)).content).toContain("Content for read test");
950
1021
  } finally {
951
1022
  await config.closeSandbox?.(tmp);
952
1023
  }
@@ -974,19 +1045,20 @@ function registerInitialFilesTests(config, timeout) {
974
1045
  await config.closeSandbox?.(tmp);
975
1046
  }
976
1047
  }, timeout);
977
- it("should make initialFiles in subdirectories visible via lsInfo()", async () => {
1048
+ it("should make initialFiles in subdirectories visible via ls()", async () => {
978
1049
  const dirPath = config.resolvePath("init-ls-dir");
979
1050
  const filePath = `${dirPath}/file.txt`;
980
1051
  const tmp = await withRetry(() => config.createSandbox({ initialFiles: { [filePath]: "ls test content" } }));
981
1052
  try {
982
- expect((await tmp.lsInfo(dirPath)).map((e) => e.path.replace(/\/$/, ""))).toContain(filePath);
1053
+ const lsResult = await adaptSandboxInstance(tmp).ls(dirPath);
1054
+ expect(lsResult.error).toBeUndefined();
1055
+ expect((lsResult.files || []).map((e) => e.path.replace(/\/$/, ""))).toContain(filePath);
983
1056
  } finally {
984
1057
  await config.closeSandbox?.(tmp);
985
1058
  }
986
1059
  }, timeout);
987
1060
  });
988
1061
  }
989
-
990
1062
  //#endregion
991
1063
  //#region src/tests/integration.ts
992
1064
  /**
@@ -996,34 +1068,37 @@ function registerIntegrationTests(getShared, config, timeout) {
996
1068
  const { describe, it, expect } = config.runner;
997
1069
  describe("integration workflows", () => {
998
1070
  it("should complete a write-read-edit-read workflow", async () => {
999
- const shared = getShared();
1071
+ const shared = adaptSandboxInstance(getShared());
1000
1072
  const filePath = config.resolvePath("intg-workflow.txt");
1001
1073
  expect((await shared.write(filePath, "Original content")).error).toBeUndefined();
1002
- expect(await shared.read(filePath)).toContain("Original content");
1074
+ expect((await shared.read(filePath)).content).toContain("Original content");
1003
1075
  expect((await shared.edit(filePath, "Original", "Modified")).error).toBeUndefined();
1004
1076
  const updatedContent = await shared.read(filePath);
1005
- expect(updatedContent).toContain("Modified content");
1006
- expect(updatedContent).not.toContain("Original");
1077
+ expect(updatedContent.content).toContain("Modified content");
1078
+ expect(updatedContent.content).not.toContain("Original");
1007
1079
  }, timeout);
1008
1080
  it("should handle complex directory operations", async () => {
1009
- const shared = getShared();
1081
+ const shared = adaptSandboxInstance(getShared());
1010
1082
  const baseDir = config.resolvePath("intg-complex");
1011
1083
  await shared.write(`${baseDir}/root.txt`, "root file");
1012
1084
  await shared.write(`${baseDir}/subdir1/file1.txt`, "file 1");
1013
1085
  await shared.write(`${baseDir}/subdir1/file2.py`, "file 2");
1014
1086
  await shared.write(`${baseDir}/subdir2/file3.txt`, "file 3");
1015
- const lsPaths = (await shared.lsInfo(baseDir)).map((info) => info.path.replace(/\/$/, ""));
1087
+ const lsResult = await shared.ls(baseDir);
1088
+ expect(lsResult.error).toBeUndefined();
1089
+ const lsPaths = (lsResult.files || []).map((info) => info.path.replace(/\/$/, ""));
1016
1090
  expect(lsPaths).toContain(`${baseDir}/root.txt`);
1017
1091
  expect(lsPaths).toContain(`${baseDir}/subdir1`);
1018
1092
  expect(lsPaths).toContain(`${baseDir}/subdir2`);
1019
- expect((await shared.globInfo("**/*.txt", baseDir)).length).toBe(3);
1020
- const grepResult = await shared.grepRaw("file", baseDir);
1021
- expect(Array.isArray(grepResult)).toBe(true);
1022
- expect(grepResult.length).toBeGreaterThanOrEqual(3);
1093
+ const globResult = await shared.glob("**/*.txt", baseDir);
1094
+ expect(globResult.error).toBeUndefined();
1095
+ expect((globResult.files || []).length).toBe(3);
1096
+ const grepResult = await shared.grep("file", baseDir);
1097
+ expect(grepResult.error).toBeUndefined();
1098
+ expect(grepResult.matches.length).toBeGreaterThanOrEqual(3);
1023
1099
  }, timeout);
1024
1100
  });
1025
1101
  }
1026
-
1027
1102
  //#endregion
1028
1103
  //#region src/sandbox.ts
1029
1104
  /**
@@ -1048,9 +1123,9 @@ function registerIntegrationTests(getShared, config, timeout) {
1048
1123
  * - write() (new file, parent dirs, existing file, special chars, unicode, long content)
1049
1124
  * - read() (basic, nonexistent, offset, limit, offset+limit, unicode, chunked)
1050
1125
  * - edit() (single/multi occurrence, replaceAll, not found, special chars, multiline, unicode)
1051
- * - lsInfo() (basic listing, empty dir, hidden files, large dir, absolute paths)
1052
- * - grepRaw() (basic search, glob filter, case sensitivity, nested dirs, unicode)
1053
- * - globInfo() (wildcard, recursive, extension filter, character classes, deeply nested)
1126
+ * - ls() (basic listing, empty dir, hidden files, large dir, absolute paths)
1127
+ * - grep() (basic search, glob filter, case sensitivity, nested dirs, unicode)
1128
+ * - glob() (wildcard, recursive, extension filter, character classes, deeply nested)
1054
1129
  * - Initial files support (basic, nested, empty)
1055
1130
  * - Integration workflows (write-read-edit, complex directory operations)
1056
1131
  * - Error handling (file not found, non-existent command)
@@ -1149,7 +1224,7 @@ function sandboxStandardTests(config) {
1149
1224
  registerIntegrationTests(getShared, config, timeout);
1150
1225
  });
1151
1226
  }
1152
-
1153
1227
  //#endregion
1154
1228
  export { withRetry as n, sandboxStandardTests as t };
1155
- //# sourceMappingURL=sandbox-BY-U6CPm.js.map
1229
+
1230
+ //# sourceMappingURL=sandbox-BbDX1T-X.js.map