koishi-plugin-ishiki-workspace 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -355,7 +355,309 @@ var WorkspaceScheme = class extends import_koishi_plugin_ishiki2.FileScheme {
355
355
 
356
356
  // src/sandbox.ts
357
357
  var import_node_path4 = __toESM(require("node:path"));
358
- var import_just_bash = require("just-bash");
358
+
359
+ // src/js-bootstrap.ts
360
+ var JAVASCRIPT_FS_BOOTSTRAP = String.raw`
361
+ (function () {
362
+ var hostKey = Object.getOwnPropertyNames(globalThis).filter(function (name) {
363
+ return name.indexOf('__jbHost_') === 0;
364
+ })[0];
365
+ if (!hostKey) throw new Error('js-exec host bridge is unavailable');
366
+ var host = globalThis[hostKey];
367
+ var fs = globalThis.fs;
368
+ function unwrap(result) {
369
+ if (!result || result.ok !== true) throw new Error(result && result.error || 'Host operation failed');
370
+ return result.value;
371
+ }
372
+ var raw = {};
373
+ var names = [
374
+ 'readFileBuffer', 'readFileSync', 'writeFileSync', 'appendFileSync',
375
+ 'statSync', 'lstatSync', 'readdirSync', 'mkdirSync', 'rmSync',
376
+ 'symlinkSync', 'readlinkSync', 'chmodSync', 'realpathSync',
377
+ 'renameSync', 'copyFileSync'
378
+ ];
379
+ for (var index = 0; index < names.length; index++) raw[names[index]] = fs[names[index]];
380
+
381
+ var errnos = {
382
+ EPERM: -1,
383
+ ENOENT: -2,
384
+ EIO: -5,
385
+ EACCES: -13,
386
+ EEXIST: -17,
387
+ ENOTDIR: -20,
388
+ EISDIR: -21,
389
+ EINVAL: -22,
390
+ ENOSPC: -28,
391
+ EROFS: -30,
392
+ ENAMETOOLONG: -36,
393
+ ENOTEMPTY: -39,
394
+ ELOOP: -40
395
+ };
396
+
397
+ function failure(error, syscall, target, destination) {
398
+ if (!(error instanceof Error)) error = new Error(String(error));
399
+ var matched = /^([A-Z][A-Z0-9]+):/.exec(error.message);
400
+ if (matched) {
401
+ error.code = matched[1];
402
+ if (errnos[error.code] !== undefined) error.errno = errnos[error.code];
403
+ }
404
+ error.syscall = syscall;
405
+ if (target !== undefined) {
406
+ error.path = String(target);
407
+ error.message = error.message.replace(/'[^']*'(?=\s*$)/, "'" + String(target).replace(/'/g, "\\'") + "'");
408
+ }
409
+ if (destination !== undefined) error.dest = String(destination);
410
+ return error;
411
+ }
412
+
413
+ function bridge(hostName, syscall, target, args, destination) {
414
+ try {
415
+ var operation = host[hostName];
416
+ if (args.length === 1) return unwrap(operation(args[0]));
417
+ if (args.length === 2) return unwrap(operation(args[0], args[1]));
418
+ if (args.length === 3) return unwrap(operation(args[0], args[1], args[2]));
419
+ return unwrap(operation(args[0], args[1], args[2], args[3]));
420
+ } catch (error) {
421
+ throw failure(error, syscall, target, destination);
422
+ }
423
+ }
424
+
425
+ function bytes(value) {
426
+ if (value && value._data instanceof Uint8Array) return Array.from(value._data);
427
+ if (value && Array.isArray(value._data)) return value._data.slice();
428
+ if (Array.isArray(value)) return value.slice();
429
+ if (value instanceof Uint8Array) return Array.from(value);
430
+ return String(value);
431
+ }
432
+
433
+ function stats(value) {
434
+ var mtime = value.mtime instanceof Date ? value.mtime : new Date(value.mtime);
435
+ var size = Number(value.size);
436
+ var result = {
437
+ dev: 0,
438
+ ino: 0,
439
+ mode: Number(value.mode),
440
+ nlink: value.isDirectory ? 2 : 1,
441
+ uid: 1000,
442
+ gid: 1000,
443
+ rdev: 0,
444
+ size: size,
445
+ blksize: 4096,
446
+ blocks: Math.ceil(size / 512),
447
+ atime: mtime,
448
+ mtime: mtime,
449
+ ctime: mtime,
450
+ birthtime: mtime,
451
+ atimeMs: mtime.getTime(),
452
+ mtimeMs: mtime.getTime(),
453
+ ctimeMs: mtime.getTime(),
454
+ birthtimeMs: mtime.getTime()
455
+ };
456
+ result.isFile = function () { return value.isFile; };
457
+ result.isDirectory = function () { return value.isDirectory; };
458
+ result.isSymbolicLink = function () { return value.isSymbolicLink; };
459
+ result.isBlockDevice = function () { return false; };
460
+ result.isCharacterDevice = function () { return false; };
461
+ result.isFIFO = function () { return false; };
462
+ result.isSocket = function () { return false; };
463
+ return result;
464
+ }
465
+
466
+ function dirent(name, parentPath, value) {
467
+ var result = { name: String(name), parentPath: String(parentPath), path: String(parentPath) };
468
+ result.isFile = function () { return value.isFile; };
469
+ result.isDirectory = function () { return value.isDirectory; };
470
+ result.isSymbolicLink = function () { return value.isSymbolicLink; };
471
+ result.isBlockDevice = function () { return false; };
472
+ result.isCharacterDevice = function () { return false; };
473
+ result.isFIFO = function () { return false; };
474
+ result.isSocket = function () { return false; };
475
+ return result;
476
+ }
477
+
478
+ function childPath(parent, name) {
479
+ return parent.charAt(parent.length - 1) === '/' ? parent + name : parent + '/' + name;
480
+ }
481
+
482
+ function readStats(path, follow) {
483
+ return bridge('fsStat', follow ? 'stat' : 'lstat', path, [path, !follow]);
484
+ }
485
+
486
+ function collect(path, relative, options, output) {
487
+ var listing = bridge('fsReaddir', 'scandir', path, [path]);
488
+ for (var index = 0; index < listing.length; index++) {
489
+ var name = listing[index];
490
+ var nested = relative === '' ? name : relative + '/' + name;
491
+ var full = childPath(path, name);
492
+ var value = readStats(full, false);
493
+ output.push(options.withFileTypes ? dirent(name, path, value) : nested);
494
+ if (options.recursive && value.isDirectory) collect(full, nested, options, output);
495
+ }
496
+ }
497
+
498
+ function readdir(path, options) {
499
+ if (typeof options === 'string') options = { encoding: options };
500
+ options = options || {};
501
+ var output = [];
502
+ collect(path, '', { recursive: Boolean(options.recursive), withFileTypes: Boolean(options.withFileTypes) }, output);
503
+ if (options.encoding === 'buffer') return output.map(function (entry) {
504
+ return typeof entry === 'string' ? Buffer.from(entry) : entry;
505
+ });
506
+ return output;
507
+ }
508
+
509
+ function firstCreated(path) {
510
+ var text = String(path);
511
+ var absolute = text.charAt(0) === '/';
512
+ var parts = text.split('/');
513
+ var current = '';
514
+ var found;
515
+ for (var index = 0; index < parts.length; index++) {
516
+ var part = parts[index];
517
+ if (part === '' || part === '.') continue;
518
+ if (current === '') current = absolute ? '/' + part : part;
519
+ else current = childPath(current, part);
520
+ if (!fs.existsSync(current)) {
521
+ found = current;
522
+ break;
523
+ }
524
+ }
525
+ return found;
526
+ }
527
+
528
+ fs.readFileBuffer = function (path) {
529
+ try {
530
+ return raw.readFileBuffer(path);
531
+ } catch (error) {
532
+ throw failure(error, 'open', path);
533
+ }
534
+ };
535
+ fs.readFileSync = function (path, options) {
536
+ try {
537
+ return raw.readFileSync(path, options);
538
+ } catch (error) {
539
+ throw failure(error, 'open', path);
540
+ }
541
+ };
542
+ fs.writeFileSync = function (path, data) {
543
+ bridge('fsWrite', 'open', path, [path, bytes(data)]);
544
+ };
545
+ fs.appendFileSync = function (path, data) {
546
+ bridge('fsAppend', 'open', path, [path, bytes(data)]);
547
+ };
548
+ fs.statSync = function (path) {
549
+ return stats(readStats(path, true));
550
+ };
551
+ fs.lstatSync = function (path) {
552
+ return stats(readStats(path, false));
553
+ };
554
+ fs.readdirSync = readdir;
555
+ fs.mkdirSync = function (path, options) {
556
+ var recursive = Boolean(options && typeof options === 'object' && options.recursive);
557
+ var created = recursive ? firstCreated(path) : undefined;
558
+ bridge('fsMkdir', 'mkdir', path, [path, recursive]);
559
+ return created;
560
+ };
561
+ fs.rmSync = function (path, options) {
562
+ bridge('fsRm', 'rm', path, [path, Boolean(options && options.recursive), Boolean(options && options.force)]);
563
+ };
564
+ fs.existsSync = function (path) {
565
+ try {
566
+ return bridge('fsExists', 'access', path, [path]);
567
+ } catch (error) {
568
+ return false;
569
+ }
570
+ };
571
+ fs.symlinkSync = function (target, path) {
572
+ bridge('fsSymlink', 'symlink', path, [target, path]);
573
+ };
574
+ fs.readlinkSync = function (path) {
575
+ return bridge('fsReadlink', 'readlink', path, [path]);
576
+ };
577
+ fs.chmodSync = function (path, mode) {
578
+ bridge('fsChmod', 'chmod', path, [path, Number(mode)]);
579
+ };
580
+ fs.realpathSync = function (path) {
581
+ return bridge('fsRealpath', 'realpath', path, [path]);
582
+ };
583
+ fs.renameSync = function (from, to) {
584
+ bridge('fsRename', 'rename', from, [from, to], to);
585
+ };
586
+ fs.copyFileSync = function (from, to) {
587
+ bridge('fsCopy', 'copyfile', from, [from, to], to);
588
+ };
589
+ fs.unlinkSync = function (path) {
590
+ bridge('fsRm', 'unlink', path, [path, false, false]);
591
+ };
592
+ fs.rmdirSync = function (path, options) {
593
+ bridge('fsRm', 'rmdir', path, [path, Boolean(options && options.recursive), false]);
594
+ };
595
+
596
+ var callbackNames = [
597
+ ['readFile', fs.readFileSync],
598
+ ['writeFile', fs.writeFileSync],
599
+ ['appendFile', fs.appendFileSync],
600
+ ['stat', fs.statSync],
601
+ ['lstat', fs.lstatSync],
602
+ ['readdir', fs.readdirSync],
603
+ ['mkdir', fs.mkdirSync],
604
+ ['rm', fs.rmSync],
605
+ ['symlink', fs.symlinkSync],
606
+ ['readlink', fs.readlinkSync],
607
+ ['chmod', fs.chmodSync],
608
+ ['realpath', fs.realpathSync],
609
+ ['rename', fs.renameSync],
610
+ ['copyFile', fs.copyFileSync],
611
+ ['unlink', fs.unlinkSync],
612
+ ['rmdir', fs.rmdirSync]
613
+ ];
614
+ function callbackify(name, sync) {
615
+ fs[name] = function () {
616
+ var args = Array.prototype.slice.call(arguments);
617
+ var callback = args.pop();
618
+ if (typeof callback !== 'function') throw new TypeError('The "cb" argument must be of type function. Received ' + typeof callback);
619
+ Promise.resolve().then(function () {
620
+ return sync.apply(fs, args);
621
+ }).then(function (value) {
622
+ callback(null, value);
623
+ }, function (error) {
624
+ callback(error);
625
+ });
626
+ };
627
+ }
628
+ for (var index = 0; index < callbackNames.length; index++) callbackify(callbackNames[index][0], callbackNames[index][1]);
629
+ fs.exists = function (path, callback) {
630
+ if (typeof callback !== 'function') throw new TypeError('The "cb" argument must be of type function. Received ' + typeof callback);
631
+ Promise.resolve().then(function () { callback(fs.existsSync(path)); });
632
+ };
633
+
634
+ var promiseNames = [
635
+ 'readFile', 'writeFile', 'appendFile', 'stat', 'lstat', 'readdir',
636
+ 'mkdir', 'rm', 'symlink', 'readlink', 'chmod', 'realpath',
637
+ 'rename', 'copyFile', 'unlink', 'rmdir'
638
+ ];
639
+ fs.promises = {};
640
+ for (var index = 0; index < promiseNames.length; index++) (function (name) {
641
+ var sync = fs[name + 'Sync'];
642
+ fs.promises[name] = function () {
643
+ var args = arguments;
644
+ return new Promise(function (resolve, reject) {
645
+ Promise.resolve().then(function () {
646
+ return sync.apply(fs, args);
647
+ }).then(resolve, reject);
648
+ });
649
+ };
650
+ })(promiseNames[index]);
651
+ fs.promises.access = function (path) {
652
+ return new Promise(function (resolve, reject) {
653
+ if (fs.existsSync(path)) resolve();
654
+ else reject(failure(new Error("ENOENT: no such file or directory, access '" + path + "'"), 'access', path));
655
+ });
656
+ };
657
+
658
+ globalThis.fs = fs;
659
+ })();
660
+ `;
359
661
 
360
662
  // src/resource-fs.ts
361
663
  var EROFS = /* @__PURE__ */ __name(() => new Error("EROFS: resource mounts are read-only"), "EROFS");
@@ -513,8 +815,9 @@ function traversable(stat2) {
513
815
  }
514
816
  __name(traversable, "traversable");
515
817
  async function createSandbox(layout, options) {
818
+ const { Bash, InMemoryFs, MountableFs, OverlayFs, ReadWriteFs } = await import("just-bash");
516
819
  const { home } = options;
517
- class Writable extends import_just_bash.ReadWriteFs {
820
+ class Writable extends ReadWriteFs {
518
821
  static {
519
822
  __name(this, "Writable");
520
823
  }
@@ -525,26 +828,26 @@ async function createSandbox(layout, options) {
525
828
  return traversable(await super.lstat(path7));
526
829
  }
527
830
  }
528
- const homeView = new import_just_bash.MountableFs({
529
- base: new import_just_bash.InMemoryFs(),
530
- mounts: [{ mountPoint: HOME_MOUNT, filesystem: new import_just_bash.OverlayFs({ root: home, mountPoint: "/", readOnly: true }) }]
831
+ const homeView = new MountableFs({
832
+ base: new InMemoryFs(),
833
+ mounts: [{ mountPoint: HOME_MOUNT, filesystem: new OverlayFs({ root: home, mountPoint: "/", readOnly: true }) }]
531
834
  });
532
- const fs3 = new import_just_bash.MountableFs({
835
+ const fs3 = new MountableFs({
533
836
  base: homeView,
534
837
  mounts: [
535
838
  { mountPoint: WORKSPACE_MOUNT, filesystem: new Writable({ root: import_node_path4.default.join(home, "workspace") }) },
536
839
  ...options.resources ? [{ mountPoint: ISHIKI_MOUNT, filesystem: new ResourceFs(options.resources) }] : [],
537
840
  ...layout.mounts.map((mount) => ({
538
841
  mountPoint: mount.target,
539
- filesystem: mount.readOnly ? new import_just_bash.OverlayFs({ root: mount.source, mountPoint: "/", readOnly: true }) : new Writable({ root: mount.source })
842
+ filesystem: mount.readOnly ? new OverlayFs({ root: mount.source, mountPoint: "/", readOnly: true }) : new Writable({ root: mount.source })
540
843
  }))
541
844
  ]
542
845
  });
543
- const bash = new import_just_bash.Bash({
846
+ const bash = new Bash({
544
847
  fs: fs3,
545
848
  cwd: layout.cwd,
546
849
  network: layout.network ? { dangerouslyAllowFullInternetAccess: true, denyPrivateRanges: true } : void 0,
547
- javascript: layout.javascript,
850
+ javascript: layout.javascript ? { bootstrap: JAVASCRIPT_FS_BOOTSTRAP } : false,
548
851
  python: layout.python,
549
852
  executionLimits: { maxOutputSize: layout.maxExecutionOutputBytes }
550
853
  });
package/lib/index.js CHANGED
@@ -323,7 +323,309 @@ var WorkspaceScheme = class extends FileScheme {
323
323
 
324
324
  // src/sandbox.ts
325
325
  import path4 from "node:path";
326
- import { Bash, InMemoryFs, MountableFs, OverlayFs, ReadWriteFs } from "just-bash";
326
+
327
+ // src/js-bootstrap.ts
328
+ var JAVASCRIPT_FS_BOOTSTRAP = String.raw`
329
+ (function () {
330
+ var hostKey = Object.getOwnPropertyNames(globalThis).filter(function (name) {
331
+ return name.indexOf('__jbHost_') === 0;
332
+ })[0];
333
+ if (!hostKey) throw new Error('js-exec host bridge is unavailable');
334
+ var host = globalThis[hostKey];
335
+ var fs = globalThis.fs;
336
+ function unwrap(result) {
337
+ if (!result || result.ok !== true) throw new Error(result && result.error || 'Host operation failed');
338
+ return result.value;
339
+ }
340
+ var raw = {};
341
+ var names = [
342
+ 'readFileBuffer', 'readFileSync', 'writeFileSync', 'appendFileSync',
343
+ 'statSync', 'lstatSync', 'readdirSync', 'mkdirSync', 'rmSync',
344
+ 'symlinkSync', 'readlinkSync', 'chmodSync', 'realpathSync',
345
+ 'renameSync', 'copyFileSync'
346
+ ];
347
+ for (var index = 0; index < names.length; index++) raw[names[index]] = fs[names[index]];
348
+
349
+ var errnos = {
350
+ EPERM: -1,
351
+ ENOENT: -2,
352
+ EIO: -5,
353
+ EACCES: -13,
354
+ EEXIST: -17,
355
+ ENOTDIR: -20,
356
+ EISDIR: -21,
357
+ EINVAL: -22,
358
+ ENOSPC: -28,
359
+ EROFS: -30,
360
+ ENAMETOOLONG: -36,
361
+ ENOTEMPTY: -39,
362
+ ELOOP: -40
363
+ };
364
+
365
+ function failure(error, syscall, target, destination) {
366
+ if (!(error instanceof Error)) error = new Error(String(error));
367
+ var matched = /^([A-Z][A-Z0-9]+):/.exec(error.message);
368
+ if (matched) {
369
+ error.code = matched[1];
370
+ if (errnos[error.code] !== undefined) error.errno = errnos[error.code];
371
+ }
372
+ error.syscall = syscall;
373
+ if (target !== undefined) {
374
+ error.path = String(target);
375
+ error.message = error.message.replace(/'[^']*'(?=\s*$)/, "'" + String(target).replace(/'/g, "\\'") + "'");
376
+ }
377
+ if (destination !== undefined) error.dest = String(destination);
378
+ return error;
379
+ }
380
+
381
+ function bridge(hostName, syscall, target, args, destination) {
382
+ try {
383
+ var operation = host[hostName];
384
+ if (args.length === 1) return unwrap(operation(args[0]));
385
+ if (args.length === 2) return unwrap(operation(args[0], args[1]));
386
+ if (args.length === 3) return unwrap(operation(args[0], args[1], args[2]));
387
+ return unwrap(operation(args[0], args[1], args[2], args[3]));
388
+ } catch (error) {
389
+ throw failure(error, syscall, target, destination);
390
+ }
391
+ }
392
+
393
+ function bytes(value) {
394
+ if (value && value._data instanceof Uint8Array) return Array.from(value._data);
395
+ if (value && Array.isArray(value._data)) return value._data.slice();
396
+ if (Array.isArray(value)) return value.slice();
397
+ if (value instanceof Uint8Array) return Array.from(value);
398
+ return String(value);
399
+ }
400
+
401
+ function stats(value) {
402
+ var mtime = value.mtime instanceof Date ? value.mtime : new Date(value.mtime);
403
+ var size = Number(value.size);
404
+ var result = {
405
+ dev: 0,
406
+ ino: 0,
407
+ mode: Number(value.mode),
408
+ nlink: value.isDirectory ? 2 : 1,
409
+ uid: 1000,
410
+ gid: 1000,
411
+ rdev: 0,
412
+ size: size,
413
+ blksize: 4096,
414
+ blocks: Math.ceil(size / 512),
415
+ atime: mtime,
416
+ mtime: mtime,
417
+ ctime: mtime,
418
+ birthtime: mtime,
419
+ atimeMs: mtime.getTime(),
420
+ mtimeMs: mtime.getTime(),
421
+ ctimeMs: mtime.getTime(),
422
+ birthtimeMs: mtime.getTime()
423
+ };
424
+ result.isFile = function () { return value.isFile; };
425
+ result.isDirectory = function () { return value.isDirectory; };
426
+ result.isSymbolicLink = function () { return value.isSymbolicLink; };
427
+ result.isBlockDevice = function () { return false; };
428
+ result.isCharacterDevice = function () { return false; };
429
+ result.isFIFO = function () { return false; };
430
+ result.isSocket = function () { return false; };
431
+ return result;
432
+ }
433
+
434
+ function dirent(name, parentPath, value) {
435
+ var result = { name: String(name), parentPath: String(parentPath), path: String(parentPath) };
436
+ result.isFile = function () { return value.isFile; };
437
+ result.isDirectory = function () { return value.isDirectory; };
438
+ result.isSymbolicLink = function () { return value.isSymbolicLink; };
439
+ result.isBlockDevice = function () { return false; };
440
+ result.isCharacterDevice = function () { return false; };
441
+ result.isFIFO = function () { return false; };
442
+ result.isSocket = function () { return false; };
443
+ return result;
444
+ }
445
+
446
+ function childPath(parent, name) {
447
+ return parent.charAt(parent.length - 1) === '/' ? parent + name : parent + '/' + name;
448
+ }
449
+
450
+ function readStats(path, follow) {
451
+ return bridge('fsStat', follow ? 'stat' : 'lstat', path, [path, !follow]);
452
+ }
453
+
454
+ function collect(path, relative, options, output) {
455
+ var listing = bridge('fsReaddir', 'scandir', path, [path]);
456
+ for (var index = 0; index < listing.length; index++) {
457
+ var name = listing[index];
458
+ var nested = relative === '' ? name : relative + '/' + name;
459
+ var full = childPath(path, name);
460
+ var value = readStats(full, false);
461
+ output.push(options.withFileTypes ? dirent(name, path, value) : nested);
462
+ if (options.recursive && value.isDirectory) collect(full, nested, options, output);
463
+ }
464
+ }
465
+
466
+ function readdir(path, options) {
467
+ if (typeof options === 'string') options = { encoding: options };
468
+ options = options || {};
469
+ var output = [];
470
+ collect(path, '', { recursive: Boolean(options.recursive), withFileTypes: Boolean(options.withFileTypes) }, output);
471
+ if (options.encoding === 'buffer') return output.map(function (entry) {
472
+ return typeof entry === 'string' ? Buffer.from(entry) : entry;
473
+ });
474
+ return output;
475
+ }
476
+
477
+ function firstCreated(path) {
478
+ var text = String(path);
479
+ var absolute = text.charAt(0) === '/';
480
+ var parts = text.split('/');
481
+ var current = '';
482
+ var found;
483
+ for (var index = 0; index < parts.length; index++) {
484
+ var part = parts[index];
485
+ if (part === '' || part === '.') continue;
486
+ if (current === '') current = absolute ? '/' + part : part;
487
+ else current = childPath(current, part);
488
+ if (!fs.existsSync(current)) {
489
+ found = current;
490
+ break;
491
+ }
492
+ }
493
+ return found;
494
+ }
495
+
496
+ fs.readFileBuffer = function (path) {
497
+ try {
498
+ return raw.readFileBuffer(path);
499
+ } catch (error) {
500
+ throw failure(error, 'open', path);
501
+ }
502
+ };
503
+ fs.readFileSync = function (path, options) {
504
+ try {
505
+ return raw.readFileSync(path, options);
506
+ } catch (error) {
507
+ throw failure(error, 'open', path);
508
+ }
509
+ };
510
+ fs.writeFileSync = function (path, data) {
511
+ bridge('fsWrite', 'open', path, [path, bytes(data)]);
512
+ };
513
+ fs.appendFileSync = function (path, data) {
514
+ bridge('fsAppend', 'open', path, [path, bytes(data)]);
515
+ };
516
+ fs.statSync = function (path) {
517
+ return stats(readStats(path, true));
518
+ };
519
+ fs.lstatSync = function (path) {
520
+ return stats(readStats(path, false));
521
+ };
522
+ fs.readdirSync = readdir;
523
+ fs.mkdirSync = function (path, options) {
524
+ var recursive = Boolean(options && typeof options === 'object' && options.recursive);
525
+ var created = recursive ? firstCreated(path) : undefined;
526
+ bridge('fsMkdir', 'mkdir', path, [path, recursive]);
527
+ return created;
528
+ };
529
+ fs.rmSync = function (path, options) {
530
+ bridge('fsRm', 'rm', path, [path, Boolean(options && options.recursive), Boolean(options && options.force)]);
531
+ };
532
+ fs.existsSync = function (path) {
533
+ try {
534
+ return bridge('fsExists', 'access', path, [path]);
535
+ } catch (error) {
536
+ return false;
537
+ }
538
+ };
539
+ fs.symlinkSync = function (target, path) {
540
+ bridge('fsSymlink', 'symlink', path, [target, path]);
541
+ };
542
+ fs.readlinkSync = function (path) {
543
+ return bridge('fsReadlink', 'readlink', path, [path]);
544
+ };
545
+ fs.chmodSync = function (path, mode) {
546
+ bridge('fsChmod', 'chmod', path, [path, Number(mode)]);
547
+ };
548
+ fs.realpathSync = function (path) {
549
+ return bridge('fsRealpath', 'realpath', path, [path]);
550
+ };
551
+ fs.renameSync = function (from, to) {
552
+ bridge('fsRename', 'rename', from, [from, to], to);
553
+ };
554
+ fs.copyFileSync = function (from, to) {
555
+ bridge('fsCopy', 'copyfile', from, [from, to], to);
556
+ };
557
+ fs.unlinkSync = function (path) {
558
+ bridge('fsRm', 'unlink', path, [path, false, false]);
559
+ };
560
+ fs.rmdirSync = function (path, options) {
561
+ bridge('fsRm', 'rmdir', path, [path, Boolean(options && options.recursive), false]);
562
+ };
563
+
564
+ var callbackNames = [
565
+ ['readFile', fs.readFileSync],
566
+ ['writeFile', fs.writeFileSync],
567
+ ['appendFile', fs.appendFileSync],
568
+ ['stat', fs.statSync],
569
+ ['lstat', fs.lstatSync],
570
+ ['readdir', fs.readdirSync],
571
+ ['mkdir', fs.mkdirSync],
572
+ ['rm', fs.rmSync],
573
+ ['symlink', fs.symlinkSync],
574
+ ['readlink', fs.readlinkSync],
575
+ ['chmod', fs.chmodSync],
576
+ ['realpath', fs.realpathSync],
577
+ ['rename', fs.renameSync],
578
+ ['copyFile', fs.copyFileSync],
579
+ ['unlink', fs.unlinkSync],
580
+ ['rmdir', fs.rmdirSync]
581
+ ];
582
+ function callbackify(name, sync) {
583
+ fs[name] = function () {
584
+ var args = Array.prototype.slice.call(arguments);
585
+ var callback = args.pop();
586
+ if (typeof callback !== 'function') throw new TypeError('The "cb" argument must be of type function. Received ' + typeof callback);
587
+ Promise.resolve().then(function () {
588
+ return sync.apply(fs, args);
589
+ }).then(function (value) {
590
+ callback(null, value);
591
+ }, function (error) {
592
+ callback(error);
593
+ });
594
+ };
595
+ }
596
+ for (var index = 0; index < callbackNames.length; index++) callbackify(callbackNames[index][0], callbackNames[index][1]);
597
+ fs.exists = function (path, callback) {
598
+ if (typeof callback !== 'function') throw new TypeError('The "cb" argument must be of type function. Received ' + typeof callback);
599
+ Promise.resolve().then(function () { callback(fs.existsSync(path)); });
600
+ };
601
+
602
+ var promiseNames = [
603
+ 'readFile', 'writeFile', 'appendFile', 'stat', 'lstat', 'readdir',
604
+ 'mkdir', 'rm', 'symlink', 'readlink', 'chmod', 'realpath',
605
+ 'rename', 'copyFile', 'unlink', 'rmdir'
606
+ ];
607
+ fs.promises = {};
608
+ for (var index = 0; index < promiseNames.length; index++) (function (name) {
609
+ var sync = fs[name + 'Sync'];
610
+ fs.promises[name] = function () {
611
+ var args = arguments;
612
+ return new Promise(function (resolve, reject) {
613
+ Promise.resolve().then(function () {
614
+ return sync.apply(fs, args);
615
+ }).then(resolve, reject);
616
+ });
617
+ };
618
+ })(promiseNames[index]);
619
+ fs.promises.access = function (path) {
620
+ return new Promise(function (resolve, reject) {
621
+ if (fs.existsSync(path)) resolve();
622
+ else reject(failure(new Error("ENOENT: no such file or directory, access '" + path + "'"), 'access', path));
623
+ });
624
+ };
625
+
626
+ globalThis.fs = fs;
627
+ })();
628
+ `;
327
629
 
328
630
  // src/resource-fs.ts
329
631
  var EROFS = /* @__PURE__ */ __name(() => new Error("EROFS: resource mounts are read-only"), "EROFS");
@@ -481,6 +783,7 @@ function traversable(stat2) {
481
783
  }
482
784
  __name(traversable, "traversable");
483
785
  async function createSandbox(layout, options) {
786
+ const { Bash, InMemoryFs, MountableFs, OverlayFs, ReadWriteFs } = await import("just-bash");
484
787
  const { home } = options;
485
788
  class Writable extends ReadWriteFs {
486
789
  static {
@@ -512,7 +815,7 @@ async function createSandbox(layout, options) {
512
815
  fs: fs3,
513
816
  cwd: layout.cwd,
514
817
  network: layout.network ? { dangerouslyAllowFullInternetAccess: true, denyPrivateRanges: true } : void 0,
515
- javascript: layout.javascript,
818
+ javascript: layout.javascript ? { bootstrap: JAVASCRIPT_FS_BOOTSTRAP } : false,
516
819
  python: layout.python,
517
820
  executionLimits: { maxOutputSize: layout.maxExecutionOutputBytes }
518
821
  });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * js-exec 的 fs 适配。just-bash 在 guest 中压平了 stat、丢弃了 readdir 选项,
3
+ * 并把桥接错误变成普通 Error;这里在用户代码加载前补齐 Node 的文件系统约定。
4
+ */
5
+ export declare const JAVASCRIPT_FS_BOOTSTRAP: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-ishiki-workspace",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Workspace tools for Ishiki: sandboxed or native shell, file tools and skills",
5
5
  "keywords": [
6
6
  "agent",
@@ -44,11 +44,11 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "koishi": "^4.18.11",
47
- "koishi-plugin-ishiki": ">=0.0.2 <0.1.0"
47
+ "koishi-plugin-ishiki": ">=0.0.4 <0.1.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "koishi": "^4.18.11",
51
- "koishi-plugin-ishiki": ">=0.0.2 <0.1.0"
51
+ "koishi-plugin-ishiki": ">=0.0.4 <0.1.0"
52
52
  },
53
53
  "koishi": {
54
54
  "description": {