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