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