sandboxedjs 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7349,15 +7349,15 @@ var Parser = class {
7349
7349
  if (this.isWord("-p")) this.next();
7350
7350
  }
7351
7351
  }
7352
- const commands11 = [this.parseCommand()];
7352
+ const commands12 = [this.parseCommand()];
7353
7353
  const stderrToo = [];
7354
7354
  while (this.isOp("|") || this.isOp("|&")) {
7355
7355
  stderrToo.push(this.next().value === "|&");
7356
7356
  this.skipNewlines();
7357
- commands11.push(this.parseCommand());
7357
+ commands12.push(this.parseCommand());
7358
7358
  }
7359
- if (commands11.length === 1 && !negated && !timed) return commands11[0];
7360
- return { type: "pipeline", commands: commands11, negated, stderrToo, timed };
7359
+ if (commands12.length === 1 && !negated && !timed) return commands12[0];
7360
+ return { type: "pipeline", commands: commands12, negated, stderrToo, timed };
7361
7361
  }
7362
7362
  // ── commands ─────────────────────────────────────────────────────────────
7363
7363
  parseCommand() {
@@ -16980,6 +16980,145 @@ var helpCmd = defineCommand({
16980
16980
  });
16981
16981
  var commands10 = [man, whatis, apropos, less, helpCmd];
16982
16982
 
16983
+ // src/bin/clipboard.ts
16984
+ init_path();
16985
+ var CLIPBOARD_DIR = "/run/clipboard";
16986
+ function selectionPath(selection) {
16987
+ return join(CLIPBOARD_DIR, selection);
16988
+ }
16989
+ function readSelection(ctx, selection) {
16990
+ try {
16991
+ return ctx.vfs.readFile(selectionPath(selection), ctx.cred);
16992
+ } catch {
16993
+ return new Uint8Array();
16994
+ }
16995
+ }
16996
+ function writeSelection(ctx, selection, bytes2) {
16997
+ try {
16998
+ ctx.vfs.mkdir(CLIPBOARD_DIR, { recursive: true, mode: 511 });
16999
+ } catch {
17000
+ }
17001
+ ctx.vfs.writeFile(selectionPath(selection), bytes2, { privileged: true, mode: 438 });
17002
+ }
17003
+ var xsel = defineCommand({
17004
+ name: "xsel",
17005
+ summary: "access an X selection",
17006
+ usage: "xsel [--clipboard|--primary|--secondary] [--input|--output|--clear]",
17007
+ async run(ctx) {
17008
+ let selection = "primary";
17009
+ let mode = "output";
17010
+ for (const arg of ctx.args) {
17011
+ switch (arg) {
17012
+ case "-b":
17013
+ case "--clipboard":
17014
+ selection = "clipboard";
17015
+ break;
17016
+ case "-p":
17017
+ case "--primary":
17018
+ selection = "primary";
17019
+ break;
17020
+ case "-s":
17021
+ case "--secondary":
17022
+ selection = "secondary";
17023
+ break;
17024
+ case "-i":
17025
+ case "--input":
17026
+ case "-a":
17027
+ case "--append":
17028
+ mode = "input";
17029
+ break;
17030
+ case "-o":
17031
+ case "--output":
17032
+ mode = "output";
17033
+ break;
17034
+ case "-c":
17035
+ case "--clear":
17036
+ mode = "clear";
17037
+ break;
17038
+ }
17039
+ }
17040
+ if (mode === "clear") {
17041
+ writeSelection(ctx, selection, new Uint8Array());
17042
+ return 0;
17043
+ }
17044
+ if (mode === "input") {
17045
+ writeSelection(ctx, selection, await ctx.stdin.readAll());
17046
+ return 0;
17047
+ }
17048
+ ctx.write(readSelection(ctx, selection));
17049
+ return 0;
17050
+ }
17051
+ });
17052
+ var xclip = defineCommand({
17053
+ name: "xclip",
17054
+ summary: "access an X selection",
17055
+ usage: "xclip [-selection clipboard|primary|secondary] [-i|-o]",
17056
+ async run(ctx) {
17057
+ let selection = "primary";
17058
+ let mode = "input";
17059
+ const args = ctx.args;
17060
+ for (let index = 0; index < args.length; index++) {
17061
+ const arg = args[index];
17062
+ if (arg === "-selection" || arg === "-sel" || arg === "--selection") {
17063
+ const value = args[++index];
17064
+ if (value === "clipboard" || value === "primary" || value === "secondary") selection = value;
17065
+ continue;
17066
+ }
17067
+ if (arg === "-i" || arg === "-in") mode = "input";
17068
+ else if (arg === "-o" || arg === "-out") mode = "output";
17069
+ }
17070
+ if (mode === "input") {
17071
+ writeSelection(ctx, selection, await ctx.stdin.readAll());
17072
+ return 0;
17073
+ }
17074
+ ctx.write(readSelection(ctx, selection));
17075
+ return 0;
17076
+ }
17077
+ });
17078
+ var pbcopy = defineCommand({
17079
+ name: "pbcopy",
17080
+ summary: "copy standard input to the clipboard",
17081
+ usage: "pbcopy",
17082
+ async run(ctx) {
17083
+ writeSelection(ctx, "clipboard", await ctx.stdin.readAll());
17084
+ return 0;
17085
+ }
17086
+ });
17087
+ var pbpaste = defineCommand({
17088
+ name: "pbpaste",
17089
+ summary: "write the clipboard to standard output",
17090
+ usage: "pbpaste",
17091
+ run(ctx) {
17092
+ ctx.write(readSelection(ctx, "clipboard"));
17093
+ return 0;
17094
+ }
17095
+ });
17096
+ var wlCopy = defineCommand({
17097
+ name: "wl-copy",
17098
+ summary: "copy standard input to the Wayland clipboard",
17099
+ usage: "wl-copy [--primary] [text...]",
17100
+ async run(ctx) {
17101
+ const selection = ctx.args.includes("--primary") || ctx.args.includes("-p") ? "primary" : "clipboard";
17102
+ const operands = ctx.args.filter((arg) => !arg.startsWith("-"));
17103
+ const bytes2 = operands.length ? new TextEncoder().encode(operands.join(" ")) : await ctx.stdin.readAll();
17104
+ writeSelection(ctx, selection, bytes2);
17105
+ return 0;
17106
+ }
17107
+ });
17108
+ var wlPaste = defineCommand({
17109
+ name: "wl-paste",
17110
+ summary: "write the Wayland clipboard to standard output",
17111
+ usage: "wl-paste [--primary] [-n]",
17112
+ run(ctx) {
17113
+ const selection = ctx.args.includes("--primary") || ctx.args.includes("-p") ? "primary" : "clipboard";
17114
+ const bytes2 = readSelection(ctx, selection);
17115
+ ctx.write(bytes2);
17116
+ if (!ctx.args.includes("-n") && !ctx.args.includes("--no-newline")) ctx.write("\n");
17117
+ return 0;
17118
+ }
17119
+ });
17120
+ var commands11 = [xsel, xclip, pbcopy, pbpaste, wlCopy, wlPaste];
17121
+
16983
17122
  // src/runtime/node.ts
16984
17123
  init_path();
16985
17124
  var NODE_VERSION = "v22.12.0";
@@ -19336,6 +19475,7 @@ function allCommands() {
19336
19475
  ...commands8,
19337
19476
  ...commands9,
19338
19477
  ...commands10,
19478
+ ...commands11,
19339
19479
  ...nodeCommands(),
19340
19480
  ...pythonCommands(),
19341
19481
  ...ffmpegCommands(),
@@ -19632,6 +19772,13 @@ var KernelChildProcess = class {
19632
19772
  } catch {
19633
19773
  }
19634
19774
  }
19775
+ /** Signal EOF, so a child reading stdin to the end can finish. */
19776
+ endStdin() {
19777
+ try {
19778
+ this.stdin.end();
19779
+ } catch {
19780
+ }
19781
+ }
19635
19782
  kill(signal = "SIGTERM") {
19636
19783
  if (this.process) {
19637
19784
  this.process.deliver(signal);
@@ -20543,6 +20690,55 @@ function splitSpecifier(specifier) {
20543
20690
  return { name: parts.slice(0, size).join("/"), subpath: parts.slice(size).join("/") };
20544
20691
  }
20545
20692
 
20693
+ // src/runtime/readable-from.ts
20694
+ function createReadableFrom(Readable) {
20695
+ return function from(source, options = {}) {
20696
+ if (source && typeof source.pipe === "function") return source;
20697
+ const iterator = source && typeof source[Symbol.asyncIterator] === "function" ? source[Symbol.asyncIterator]() : source && typeof source[Symbol.iterator] === "function" ? source[Symbol.iterator]() : (function* () {
20698
+ yield source;
20699
+ })();
20700
+ let reading = false;
20701
+ const stream = new Readable({
20702
+ ...options,
20703
+ objectMode: options.objectMode ?? false,
20704
+ read() {
20705
+ if (reading) return;
20706
+ reading = true;
20707
+ void (async () => {
20708
+ try {
20709
+ for (; ; ) {
20710
+ const next = await iterator.next();
20711
+ if (next.done) {
20712
+ stream.push(null);
20713
+ return;
20714
+ }
20715
+ const value = next.value;
20716
+ if (!stream.push(value === void 0 ? null : value)) return;
20717
+ }
20718
+ } catch (error) {
20719
+ stream.destroy(error);
20720
+ } finally {
20721
+ reading = false;
20722
+ }
20723
+ })();
20724
+ },
20725
+ destroy(error, callback) {
20726
+ void (async () => {
20727
+ try {
20728
+ await iterator.return?.();
20729
+ } catch {
20730
+ }
20731
+ callback(error);
20732
+ })();
20733
+ }
20734
+ });
20735
+ return stream;
20736
+ };
20737
+ }
20738
+ function installReadableFrom(streamModule5) {
20739
+ streamModule5.Readable.from = createReadableFrom(streamModule5.Readable);
20740
+ }
20741
+
20546
20742
  // src/runtime/util-module.ts
20547
20743
  var customInspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
20548
20744
  var promisifyCustom = /* @__PURE__ */ Symbol.for("nodejs.util.promisify.custom");
@@ -21430,6 +21626,10 @@ var VirtualHttpRouter = class {
21430
21626
  unregister(port, server) {
21431
21627
  if (this.servers.get(port)?.server === server) this.servers.delete(port);
21432
21628
  }
21629
+ /** Whether anything in this container is listening on `port`. */
21630
+ activePortsIncludes(port) {
21631
+ return this.servers.has(port);
21632
+ }
21433
21633
  activePorts(owner) {
21434
21634
  return [...this.servers].filter(([, item]) => owner === void 0 || item.owner === owner).map(([port]) => port).sort((a, b) => a - b);
21435
21635
  }
@@ -21453,13 +21653,269 @@ var VirtualHttpRouter = class {
21453
21653
  }
21454
21654
  }
21455
21655
  };
21456
- function createHttpModule(router, owner) {
21656
+ var VirtualClientResponse = class extends streamModule4__default.default.Readable {
21657
+ constructor(statusCode, statusMessage, headers, body) {
21658
+ super();
21659
+ this.statusCode = statusCode;
21660
+ this.statusMessage = statusMessage;
21661
+ this.headers = headers;
21662
+ this.rawHeaders = Object.entries(headers).flatMap(([key, value]) => [key, value]);
21663
+ if (body.length) this.push(Buffer2.from(body));
21664
+ this.push(null);
21665
+ this.complete = true;
21666
+ }
21667
+ statusCode;
21668
+ statusMessage;
21669
+ headers;
21670
+ httpVersion = "1.1";
21671
+ httpVersionMajor = 1;
21672
+ httpVersionMinor = 1;
21673
+ socket = socketStub();
21674
+ connection = this.socket;
21675
+ rawHeaders;
21676
+ complete = false;
21677
+ _read() {
21678
+ }
21679
+ setTimeout(_milliseconds, callback) {
21680
+ if (callback) this.once("timeout", callback);
21681
+ return this;
21682
+ }
21683
+ };
21684
+ function resolveTarget(input, overrides, defaultProtocol) {
21685
+ const options = {};
21686
+ if (typeof input === "string" || input instanceof URL) {
21687
+ const url = new URL(String(input));
21688
+ options.protocol = url.protocol;
21689
+ options.hostname = url.hostname;
21690
+ if (url.port) options.port = Number(url.port);
21691
+ options.path = `${url.pathname}${url.search}`;
21692
+ if (url.username || url.password) options.auth = `${url.username}:${url.password}`;
21693
+ } else if (input && typeof input === "object") {
21694
+ Object.assign(options, input);
21695
+ }
21696
+ if (overrides) Object.assign(options, overrides);
21697
+ const protocol = String(options.protocol ?? defaultProtocol).replace(/:?$/, ":");
21698
+ const headers = {};
21699
+ for (const [name, value] of Object.entries(options.headers ?? {})) {
21700
+ if (value === void 0 || value === null) continue;
21701
+ headers[String(name)] = Array.isArray(value) ? value.join(", ") : String(value);
21702
+ }
21703
+ if (options.auth && headers.authorization === void 0) {
21704
+ headers.authorization = `Basic ${Buffer2.from(String(options.auth)).toString("base64")}`;
21705
+ }
21706
+ const rawHost = String(options.hostname ?? options.host ?? "localhost");
21707
+ const hostMatch = /^(\[[^\]]*\]|[^:]*)(?::(\d+))?$/.exec(rawHost);
21708
+ const hostname = (hostMatch?.[1] ?? rawHost).replace(/^\[|\]$/g, "");
21709
+ const explicitPort = options.port ?? hostMatch?.[2];
21710
+ const port = explicitPort === void 0 || explicitPort === null || explicitPort === "" || Number.isNaN(Number(explicitPort)) ? protocol === "https:" ? 443 : 80 : Number(explicitPort);
21711
+ return {
21712
+ protocol,
21713
+ hostname,
21714
+ port,
21715
+ path: String(options.path ?? "/"),
21716
+ method: String(options.method ?? "GET").toUpperCase(),
21717
+ headers
21718
+ };
21719
+ }
21720
+ function isLoopback(hostname) {
21721
+ return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "::" || hostname.startsWith("127.") || hostname.endsWith(".localhost");
21722
+ }
21723
+ var VirtualClientRequest = class extends streamModule4__default.default.Writable {
21724
+ constructor(target, router, fetchImpl, trackRequest) {
21725
+ super();
21726
+ this.target = target;
21727
+ this.router = router;
21728
+ this.fetchImpl = fetchImpl;
21729
+ this.trackRequest = trackRequest;
21730
+ for (const [name, value] of Object.entries(target.headers)) this.setHeader(name, value);
21731
+ }
21732
+ target;
21733
+ router;
21734
+ fetchImpl;
21735
+ trackRequest;
21736
+ socket = socketStub();
21737
+ connection = this.socket;
21738
+ chunks = [];
21739
+ headerMap = /* @__PURE__ */ new Map();
21740
+ dispatched = false;
21741
+ destroyedByUser = false;
21742
+ timer;
21743
+ aborted = false;
21744
+ finished = false;
21745
+ reusedSocket = false;
21746
+ get method() {
21747
+ return this.target.method;
21748
+ }
21749
+ get path() {
21750
+ return this.target.path;
21751
+ }
21752
+ get host() {
21753
+ return this.target.hostname;
21754
+ }
21755
+ get protocol() {
21756
+ return this.target.protocol;
21757
+ }
21758
+ setHeader(name, value) {
21759
+ validateHeaderName(name);
21760
+ validateHeaderValue(name, value);
21761
+ this.headerMap.set(name.toLowerCase(), { name, value: Array.isArray(value) ? [...value].map(String).join(", ") : String(value) });
21762
+ return this;
21763
+ }
21764
+ getHeader(name) {
21765
+ return this.headerMap.get(name.toLowerCase())?.value;
21766
+ }
21767
+ getHeaders() {
21768
+ return Object.fromEntries([...this.headerMap].map(([key, entry]) => [key, entry.value]));
21769
+ }
21770
+ getHeaderNames() {
21771
+ return [...this.headerMap.keys()];
21772
+ }
21773
+ hasHeader(name) {
21774
+ return this.headerMap.has(name.toLowerCase());
21775
+ }
21776
+ removeHeader(name) {
21777
+ this.headerMap.delete(name.toLowerCase());
21778
+ }
21779
+ flushHeaders() {
21780
+ }
21781
+ setNoDelay() {
21782
+ }
21783
+ setSocketKeepAlive() {
21784
+ }
21785
+ ref() {
21786
+ return this;
21787
+ }
21788
+ unref() {
21789
+ return this;
21790
+ }
21791
+ setTimeout(milliseconds, callback) {
21792
+ if (callback) this.once("timeout", callback);
21793
+ clearTimeout(this.timer);
21794
+ this.timer = setTimeout(() => this.emit("timeout"), milliseconds);
21795
+ return this;
21796
+ }
21797
+ abort() {
21798
+ this.destroyRequest();
21799
+ }
21800
+ destroy(error) {
21801
+ this.destroyRequest(error);
21802
+ return this;
21803
+ }
21804
+ destroyRequest(error) {
21805
+ if (this.destroyedByUser) return;
21806
+ this.destroyedByUser = true;
21807
+ this.aborted = true;
21808
+ clearTimeout(this.timer);
21809
+ this.emit("abort");
21810
+ if (error) this.emit("error", error);
21811
+ this.emit("close");
21812
+ }
21813
+ _write(chunk, encoding, callback) {
21814
+ this.chunks.push(Buffer2.isBuffer(chunk) ? chunk : Buffer2.from(chunk, encoding));
21815
+ callback();
21816
+ }
21817
+ _final(callback) {
21818
+ this.finished = true;
21819
+ callback();
21820
+ void this.dispatch();
21821
+ }
21822
+ async dispatch() {
21823
+ if (this.dispatched || this.destroyedByUser) return;
21824
+ this.dispatched = true;
21825
+ const body = Buffer2.concat(this.chunks);
21826
+ const release = this.trackRequest?.();
21827
+ let released = false;
21828
+ const untrack = () => {
21829
+ if (released || !release) return;
21830
+ released = true;
21831
+ release();
21832
+ };
21833
+ let settled = false;
21834
+ try {
21835
+ const response = this.router.activePortsIncludes(this.target.port) && isLoopback(this.target.hostname) ? await this.viaRouter(body) : await this.viaFetch(body);
21836
+ if (this.destroyedByUser) return;
21837
+ clearTimeout(this.timer);
21838
+ settled = true;
21839
+ response.once("end", untrack);
21840
+ response.once("close", untrack);
21841
+ response.once("error", untrack);
21842
+ this.emit("response", response);
21843
+ queueMicrotask(() => {
21844
+ if (this.listenerCount("response") === 0) response.resume();
21845
+ });
21846
+ } catch (error) {
21847
+ clearTimeout(this.timer);
21848
+ if (this.destroyedByUser) return;
21849
+ const failure = error instanceof Error ? error : new Error(String(error));
21850
+ if (!("code" in failure)) Object.assign(failure, { code: "ECONNREFUSED" });
21851
+ this.emit("error", failure);
21852
+ } finally {
21853
+ if (!settled) untrack();
21854
+ }
21855
+ }
21856
+ async viaRouter(body) {
21857
+ const result = await this.router.request(this.target.port, {
21858
+ method: this.target.method,
21859
+ path: this.target.path,
21860
+ headers: this.getHeaders(),
21861
+ body: body.length ? new Uint8Array(body) : null
21862
+ });
21863
+ const body_ = result.body;
21864
+ const bytes2 = typeof body_ === "string" ? new TextEncoder().encode(body_) : body_ instanceof ArrayBuffer ? new Uint8Array(body_) : body_ ?? new Uint8Array();
21865
+ const status = result.statusCode ?? 200;
21866
+ return new VirtualClientResponse(status, result.statusMessage ?? STATUS_CODES[status] ?? "", lowercaseHeaders(result.headers), bytes2);
21867
+ }
21868
+ async viaFetch(body) {
21869
+ const fetchImpl = this.fetchImpl ?? globalThis.fetch;
21870
+ if (typeof fetchImpl !== "function") {
21871
+ throw Object.assign(new Error(`getaddrinfo ENOTFOUND ${this.target.hostname}`), { code: "ENOTFOUND" });
21872
+ }
21873
+ const portSuffix = this.target.protocol === "https:" && this.target.port === 443 || this.target.protocol === "http:" && this.target.port === 80 ? "" : `:${this.target.port}`;
21874
+ const url = `${this.target.protocol}//${this.target.hostname}${portSuffix}${this.target.path}`;
21875
+ const bodyless = this.target.method === "GET" || this.target.method === "HEAD";
21876
+ const headers = this.getHeaders();
21877
+ delete headers.host;
21878
+ delete headers.connection;
21879
+ delete headers["content-length"];
21880
+ const response = await fetchImpl(url, {
21881
+ method: this.target.method,
21882
+ headers,
21883
+ ...bodyless || body.length === 0 ? {} : { body: new Uint8Array(body) },
21884
+ redirect: "follow"
21885
+ });
21886
+ const received = {};
21887
+ response.headers.forEach((value, key) => {
21888
+ received[key.toLowerCase()] = value;
21889
+ });
21890
+ const bytes2 = new Uint8Array(await response.arrayBuffer());
21891
+ return new VirtualClientResponse(response.status, response.statusText, received, bytes2);
21892
+ }
21893
+ };
21894
+ function lowercaseHeaders(headers) {
21895
+ return Object.fromEntries(Object.entries(headers ?? {}).map(([key, value]) => [key.toLowerCase(), String(value)]));
21896
+ }
21897
+ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:") {
21457
21898
  const createServer = (listener) => new VirtualHttpServer(router, owner, listener);
21458
- const module = {
21899
+ const request = (input, second, third) => {
21900
+ const overrides = second && typeof second === "object" && !(second instanceof Function) ? second : void 0;
21901
+ const callback = [second, third].find((value) => typeof value === "function");
21902
+ const req = new VirtualClientRequest(resolveTarget(input, overrides, defaultProtocol), router, options.fetch, options.trackRequest);
21903
+ if (callback) req.on("response", callback);
21904
+ return req;
21905
+ };
21906
+ const get = (input, second, third) => {
21907
+ const req = request(input, second, third);
21908
+ req.end();
21909
+ return req;
21910
+ };
21911
+ return {
21459
21912
  createServer,
21913
+ request,
21914
+ get,
21460
21915
  Server: VirtualHttpServer,
21461
21916
  ServerResponse: VirtualServerResponse,
21462
21917
  IncomingMessage: VirtualIncomingMessage,
21918
+ ClientRequest: VirtualClientRequest,
21463
21919
  METHODS,
21464
21920
  STATUS_CODES,
21465
21921
  maxHeaderSize: 16 * 1024,
@@ -21469,10 +21925,6 @@ function createHttpModule(router, owner) {
21469
21925
  Agent: class Agent {
21470
21926
  }
21471
21927
  };
21472
- return { ...module, request: unsupportedClient, get: unsupportedClient };
21473
- }
21474
- function unsupportedClient() {
21475
- throw Object.assign(new Error("http client requests are not implemented yet"), { code: "ERR_NOT_IMPLEMENTED" });
21476
21928
  }
21477
21929
  function validateHeaderName(name) {
21478
21930
  if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) throw Object.assign(new TypeError(`Header name must be a valid HTTP token [${name}]`), { code: "ERR_INVALID_HTTP_TOKEN" });
@@ -21612,6 +22064,13 @@ var ChildProcess = class extends EventEmitter4__default.default {
21612
22064
  } catch {
21613
22065
  }
21614
22066
  done();
22067
+ },
22068
+ final: (done) => {
22069
+ try {
22070
+ handle.endStdin?.();
22071
+ } catch {
22072
+ }
22073
+ done();
21615
22074
  }
21616
22075
  });
21617
22076
  this.stdio = [this.stdin, this.stdout, this.stderr];
@@ -21850,7 +22309,7 @@ function emitKeypressEvents(stream) {
21850
22309
  }
21851
22310
  });
21852
22311
  }
21853
- function createReadlineModule(defaultInput) {
22312
+ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
21854
22313
  class Interface extends EventEmitter4__default.default {
21855
22314
  constructor(input, output, terminal = false) {
21856
22315
  super();
@@ -21861,6 +22320,14 @@ function createReadlineModule(defaultInput) {
21861
22320
  if (terminal) {
21862
22321
  emitKeypressEvents(input);
21863
22322
  input?.on?.("keypress", this.onTerminalKeypress);
22323
+ if (typeof input?.setRawMode === "function") {
22324
+ try {
22325
+ input.setRawMode(true);
22326
+ this.ownsRawMode = true;
22327
+ } catch {
22328
+ }
22329
+ }
22330
+ input?.resume?.();
21864
22331
  } else {
21865
22332
  input?.on?.("data", (chunk) => this.receive(String(chunk)));
21866
22333
  }
@@ -21872,6 +22339,10 @@ function createReadlineModule(defaultInput) {
21872
22339
  buffer = "";
21873
22340
  pending = [];
21874
22341
  lines = [];
22342
+ /** Set while `question` is waiting, so the echoed line carries the query. */
22343
+ questionPrompt = null;
22344
+ /** Raw mode is only ours to restore if we were the one to enable it. */
22345
+ ownsRawMode = false;
21875
22346
  line = "";
21876
22347
  cursor = 0;
21877
22348
  terminal;
@@ -21879,20 +22350,31 @@ function createReadlineModule(defaultInput) {
21879
22350
  /** Split incoming data into lines, answering any waiting `question`. */
21880
22351
  receive(text2) {
21881
22352
  this.buffer += text2;
21882
- let newline = this.buffer.indexOf("\n");
21883
- while (newline >= 0) {
21884
- const line = this.buffer.slice(0, newline).replace(/\r$/, "");
21885
- this.buffer = this.buffer.slice(newline + 1);
21886
- const waiting = this.pending.shift();
21887
- if (waiting) waiting(line);
21888
- else {
21889
- this.lines.push(line);
21890
- this.emit("line", line);
21891
- }
21892
- newline = this.buffer.indexOf("\n");
22353
+ let match2 = /\r\n|\r|\n/.exec(this.buffer);
22354
+ while (match2) {
22355
+ const line = this.buffer.slice(0, match2.index);
22356
+ this.buffer = this.buffer.slice(match2.index + match2[0].length);
22357
+ this.deliver(line);
22358
+ match2 = /\r\n|\r|\n/.exec(this.buffer);
22359
+ }
22360
+ }
22361
+ /** Hand a completed line to whoever is waiting for it. */
22362
+ deliver(line) {
22363
+ const waiting = this.pending.shift();
22364
+ if (waiting) {
22365
+ this.questionPrompt = null;
22366
+ waiting(line);
22367
+ return;
21893
22368
  }
22369
+ this.lines.push(line);
22370
+ this.emit("line", line);
21894
22371
  }
21895
22372
  question(query, callback) {
22373
+ if (this.terminal) {
22374
+ this.questionPrompt = query;
22375
+ this.line = "";
22376
+ this.cursor = 0;
22377
+ }
21896
22378
  this.output?.write?.(query);
21897
22379
  if (callback) {
21898
22380
  this.pending.push(callback);
@@ -21907,6 +22389,9 @@ function createReadlineModule(defaultInput) {
21907
22389
  setPrompt(text2) {
21908
22390
  this.promptText = text2;
21909
22391
  }
22392
+ getPrompt() {
22393
+ return this.promptText;
22394
+ }
21910
22395
  pause() {
21911
22396
  this.input?.pause?.();
21912
22397
  return this;
@@ -21934,21 +22419,48 @@ function createReadlineModule(defaultInput) {
21934
22419
  if (text2) {
21935
22420
  this.line = this.line.slice(0, this.cursor) + text2 + this.line.slice(this.cursor);
21936
22421
  this.cursor += text2.length;
22422
+ this.refresh();
21937
22423
  }
21938
22424
  }
21939
22425
  getCursorPos() {
21940
- return { rows: 0, cols: this.cursor + this.promptText.length };
22426
+ return { rows: 0, cols: this.cursor + this.currentPrompt().length };
21941
22427
  }
21942
22428
  close() {
21943
22429
  if (this.closed) return;
21944
22430
  this.closed = true;
21945
22431
  if (this.terminal) {
21946
22432
  this.input?.off?.("keypress", this.onTerminalKeypress);
22433
+ if (this.ownsRawMode) {
22434
+ this.ownsRawMode = false;
22435
+ try {
22436
+ this.input?.setRawMode?.(false);
22437
+ } catch {
22438
+ }
22439
+ }
21947
22440
  this.input?.pause?.();
21948
22441
  }
21949
22442
  for (const waiting of this.pending.splice(0)) waiting("");
21950
22443
  this.emit("close");
21951
22444
  }
22445
+ /** The text shown to the left of the edited line. */
22446
+ currentPrompt() {
22447
+ return this.questionPrompt ?? this.promptText;
22448
+ }
22449
+ /**
22450
+ * Repaint the edited line.
22451
+ *
22452
+ * In raw mode nothing echoes on its own, so an Interface that does not do
22453
+ * this leaves the user typing blind. Libraries that draw their own frame
22454
+ * (clack, inquirer) simply overwrite this, exactly as they do on Node.
22455
+ */
22456
+ refresh() {
22457
+ if (!this.terminal) return;
22458
+ const prompt = this.currentPrompt();
22459
+ let text2 = `\r\x1B[0K${prompt}${this.line}`;
22460
+ const back = this.line.length - this.cursor;
22461
+ if (back > 0) text2 += `\x1B[${back}D`;
22462
+ this.output?.write?.(text2);
22463
+ }
21952
22464
  edit(sequence, key) {
21953
22465
  if (this.closed) return;
21954
22466
  const name = key.name;
@@ -21956,48 +22468,62 @@ function createReadlineModule(defaultInput) {
21956
22468
  const value = this.line;
21957
22469
  this.line = "";
21958
22470
  this.cursor = 0;
21959
- this.emit("line", value);
22471
+ this.output?.write?.("\r\n");
22472
+ this.deliver(value);
21960
22473
  return;
21961
22474
  }
21962
22475
  if (name === "left") {
21963
22476
  this.cursor = Math.max(0, this.cursor - 1);
22477
+ this.refresh();
21964
22478
  return;
21965
22479
  }
21966
22480
  if (name === "right") {
21967
22481
  this.cursor = Math.min(this.line.length, this.cursor + 1);
22482
+ this.refresh();
21968
22483
  return;
21969
22484
  }
21970
22485
  if (name === "home" || key.ctrl && name === "a") {
21971
22486
  this.cursor = 0;
22487
+ this.refresh();
21972
22488
  return;
21973
22489
  }
21974
22490
  if (name === "end" || key.ctrl && name === "e") {
21975
22491
  this.cursor = this.line.length;
22492
+ this.refresh();
21976
22493
  return;
21977
22494
  }
21978
22495
  if (name === "backspace" || key.ctrl && name === "h") {
21979
22496
  if (this.cursor > 0) {
21980
22497
  this.line = this.line.slice(0, this.cursor - 1) + this.line.slice(this.cursor);
21981
22498
  this.cursor--;
22499
+ this.refresh();
21982
22500
  }
21983
22501
  return;
21984
22502
  }
21985
22503
  if (name === "delete") {
21986
- if (this.cursor < this.line.length) this.line = this.line.slice(0, this.cursor) + this.line.slice(this.cursor + 1);
22504
+ if (this.cursor < this.line.length) {
22505
+ this.line = this.line.slice(0, this.cursor) + this.line.slice(this.cursor + 1);
22506
+ this.refresh();
22507
+ }
21987
22508
  return;
21988
22509
  }
21989
22510
  if (key.ctrl && name === "u") {
21990
22511
  this.line = this.line.slice(this.cursor);
21991
22512
  this.cursor = 0;
22513
+ this.refresh();
21992
22514
  return;
21993
22515
  }
21994
22516
  if (key.ctrl && name === "k") {
21995
22517
  this.line = this.line.slice(0, this.cursor);
22518
+ this.refresh();
21996
22519
  return;
21997
22520
  }
21998
22521
  if (key.ctrl || key.meta || sequence.length !== 1 || sequence < " ") return;
22522
+ const atEnd = this.cursor === this.line.length;
21999
22523
  this.line = this.line.slice(0, this.cursor) + sequence + this.line.slice(this.cursor);
22000
22524
  this.cursor += sequence.length;
22525
+ if (atEnd) this.output?.write?.(sequence);
22526
+ else this.refresh();
22001
22527
  }
22002
22528
  async *[Symbol.asyncIterator]() {
22003
22529
  while (!this.closed || this.lines.length) {
@@ -22027,13 +22553,23 @@ function createReadlineModule(defaultInput) {
22027
22553
  }
22028
22554
  }
22029
22555
  }
22556
+ const isTerminal = (options, output) => {
22557
+ if (typeof options?.terminal === "boolean") return options.terminal;
22558
+ return Boolean(output?.isTTY);
22559
+ };
22030
22560
  const createInterface = (options, output) => {
22031
22561
  if (options && typeof options === "object" && !options.on) {
22032
- const instance = new Interface(options.input ?? defaultInput(), options.output, options.terminal === true);
22562
+ const resolvedOutput2 = options.output ?? defaultOutput();
22563
+ const instance = new Interface(
22564
+ options.input ?? defaultInput(),
22565
+ resolvedOutput2,
22566
+ isTerminal(options, resolvedOutput2)
22567
+ );
22033
22568
  if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
22034
22569
  return instance;
22035
22570
  }
22036
- return new Interface(options ?? defaultInput(), output, Boolean(output));
22571
+ const resolvedOutput = output ?? defaultOutput();
22572
+ return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal(void 0, resolvedOutput));
22037
22573
  };
22038
22574
  const noop = () => {
22039
22575
  };
@@ -22050,6 +22586,7 @@ function createReadlineModule(defaultInput) {
22050
22586
  }
22051
22587
 
22052
22588
  // src/runtime/core-modules.ts
22589
+ installReadableFrom(streamModule4__default.default);
22053
22590
  var Dirent = class {
22054
22591
  /** Node 20+ exposes the containing directory, and `fs.glob` consumers read it. */
22055
22592
  constructor(name, stat2, parentPath = "") {
@@ -22255,9 +22792,20 @@ function createCoreModules(options) {
22255
22792
  throw new Error("createRequire is only available inside a loaded module");
22256
22793
  }
22257
22794
  };
22258
- const http = options.http ? createHttpModule(options.http.router, options.http.owner) : createUnsupportedModule("http");
22795
+ let inFlightRequests = 0;
22796
+ const httpOptions = {
22797
+ ...options.http?.fetch ? { fetch: options.http.fetch } : {},
22798
+ trackRequest: () => {
22799
+ inFlightRequests++;
22800
+ return () => {
22801
+ inFlightRequests--;
22802
+ };
22803
+ }
22804
+ };
22805
+ const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
22806
+ const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
22259
22807
  const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd) : createUnsupportedModule("child_process");
22260
- const readline = createReadlineModule(() => processObject.stdin);
22808
+ const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
22261
22809
  const dns = createDnsModule();
22262
22810
  const builtins = {
22263
22811
  assert: assert_module_default,
@@ -22275,7 +22823,7 @@ function createCoreModules(options) {
22275
22823
  "fs/promises": fs.promises,
22276
22824
  module: moduleBuiltin,
22277
22825
  http,
22278
- https: http,
22826
+ https,
22279
22827
  os,
22280
22828
  path,
22281
22829
  "path/posix": path,
@@ -22315,6 +22863,8 @@ function createCoreModules(options) {
22315
22863
  process: processObject,
22316
22864
  pendingHandles: timers.pending,
22317
22865
  pendingUnrefed: timers.pendingUnrefed,
22866
+ /** Client requests sent but not yet read to completion. */
22867
+ pendingRequests: () => inFlightRequests,
22318
22868
  writeStdin: (data) => {
22319
22869
  if (options.interactiveStdin) stdin.write(data);
22320
22870
  },
@@ -22542,9 +23092,20 @@ function createFsModule(volume, cwd, stdinPath) {
22542
23092
  if (position == null) file3.position = start2 + chunk.length;
22543
23093
  return chunk.length;
22544
23094
  },
22545
- createReadStream: (path) => {
22546
- const data = Buffer2.from(volume.readFileSync(resolveLinks(volume, abs(path))));
22547
- return streamModule4__default.default.Readable.from([data]);
23095
+ createReadStream: (path, options) => {
23096
+ const target = abs(path);
23097
+ const settings = typeof options === "string" ? { encoding: options } : options ?? {};
23098
+ const whole = Buffer2.from(volume.readFileSync(resolveLinks(volume, target)));
23099
+ const start2 = settings.start ?? 0;
23100
+ const end = settings.end === void 0 ? whole.length : Math.min(settings.end + 1, whole.length);
23101
+ const slice = whole.subarray(start2, Math.max(start2, end));
23102
+ const stream = streamModule4__default.default.Readable.from([settings.encoding ? slice.toString(settings.encoding) : slice]);
23103
+ Object.assign(stream, { path: target, bytesRead: slice.length, close: () => stream.destroy() });
23104
+ queueMicrotask(() => {
23105
+ stream.emit("open", 0);
23106
+ stream.emit("ready");
23107
+ });
23108
+ return stream;
22548
23109
  },
22549
23110
  createWriteStream: (path, options) => {
22550
23111
  const target = abs(path);
@@ -23661,6 +24222,11 @@ var PodChildProcess = class {
23661
24222
  started = false;
23662
24223
  cancelled = false;
23663
24224
  child;
24225
+ /* The child is launched asynchronously, but a parent may write and close
24226
+ * its input synchronously right after spawning. Holding both until the
24227
+ * child exists is what keeps `echo hi | child` from losing the `hi`. */
24228
+ pendingInput = "";
24229
+ inputEnded = false;
23664
24230
  on(event, listener) {
23665
24231
  let set = this.listeners.get(event);
23666
24232
  if (!set) this.listeners.set(event, set = /* @__PURE__ */ new Set());
@@ -23683,6 +24249,11 @@ var PodChildProcess = class {
23683
24249
  (child) => {
23684
24250
  this.child = child;
23685
24251
  if (this.cancelled) child.kill();
24252
+ if (this.pendingInput !== "") {
24253
+ child.write(this.pendingInput);
24254
+ this.pendingInput = "";
24255
+ }
24256
+ if (this.inputEnded) child.endInput?.();
23686
24257
  child.on("output", (text2) => {
23687
24258
  this.stdout += text2;
23688
24259
  this.emit("stdout", text2);
@@ -23705,7 +24276,13 @@ var PodChildProcess = class {
23705
24276
  });
23706
24277
  }
23707
24278
  sendStdin(data) {
23708
- this.child?.write(data);
24279
+ if (this.child) this.child.write(data);
24280
+ else this.pendingInput += data;
24281
+ }
24282
+ /** Signal EOF, so a child reading stdin to the end can finish. */
24283
+ endStdin() {
24284
+ if (this.child) this.child.endInput?.();
24285
+ else this.inputEnded = true;
23709
24286
  }
23710
24287
  kill(signal = "SIGTERM") {
23711
24288
  if (this.child) this.child.kill(signal);
@@ -23788,6 +24365,8 @@ var LocalRuntimePod = class _LocalRuntimePod {
23788
24365
  modules;
23789
24366
  esbuild;
23790
24367
  rolldownBinding;
24368
+ /** Backs outbound `http`/`https` client requests from inside the sandbox. */
24369
+ fetch;
23791
24370
  constructor(options) {
23792
24371
  ensureProcessGlobal();
23793
24372
  this.workdir = options.workdir ?? "/";
@@ -23802,6 +24381,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23802
24381
  const notify = options.onServerReady;
23803
24382
  this.router.onListen = (port) => notify(port, `http://localhost:${port}`);
23804
24383
  }
24384
+ if (options.fetch) this.fetch = options.fetch;
23805
24385
  this.packages = new CleanPackageInstaller(this.volume, {
23806
24386
  cwd: this.workdir,
23807
24387
  ...options.registry ? { registry: options.registry } : {},
@@ -23841,7 +24421,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23841
24421
  proc.exitNow(code);
23842
24422
  if (engine?.isEvaluating) throw new ProcessExit(code);
23843
24423
  },
23844
- http: { router: this.router, owner },
24424
+ http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
23845
24425
  spawnChild: (config) => this.processManager.spawn(config),
23846
24426
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
23847
24427
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
@@ -23858,7 +24438,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
23858
24438
  });
23859
24439
  try {
23860
24440
  await engine.run(script);
23861
- await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin);
24441
+ await this.settle(owner, core.pendingHandles, core.pendingUnrefed, core.readingStdin, core.pendingRequests);
23862
24442
  if (this.router.activePorts(owner).length) {
23863
24443
  await proc.waitForKill();
23864
24444
  return 137;
@@ -23904,18 +24484,18 @@ var LocalRuntimePod = class _LocalRuntimePod {
23904
24484
  * A plain script that has genuinely finished falls straight through both,
23905
24485
  * costing a handful of empty turns.
23906
24486
  */
23907
- async settle(owner, pendingHandles, pendingUnrefed, readingStdin) {
24487
+ async settle(owner, pendingHandles, pendingUnrefed, readingStdin, pendingRequests) {
23908
24488
  for (let turn = 0; turn < DRAIN_TURNS; turn++) {
23909
24489
  if (this.router.activePorts(owner).length) return;
23910
24490
  await new Promise((resolve2) => setTimeout(resolve2, 0));
23911
24491
  }
23912
- if (pendingHandles() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
24492
+ if (pendingHandles() === 0 && pendingRequests() === 0 && pendingUnrefed() > 0 && !readingStdin()) {
23913
24493
  const deadline = Date.now() + 1e3;
23914
24494
  while (!this.router.activePorts(owner).length && Date.now() < deadline) {
23915
24495
  await new Promise((resolve2) => setTimeout(resolve2, 5));
23916
24496
  }
23917
24497
  }
23918
- while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || readingStdin())) {
24498
+ while (!this.router.activePorts(owner).length && (pendingHandles() > 0 || pendingRequests() > 0 || readingStdin())) {
23919
24499
  await new Promise((resolve2) => setTimeout(resolve2, 5));
23920
24500
  }
23921
24501
  }
@@ -24576,17 +25156,18 @@ var Terminal = class {
24576
25156
  // ── key handling ──────────────────────────────────────────────────────────
24577
25157
  key(ch) {
24578
25158
  if (this.running) {
24579
- if (ch === CTRL_C) {
25159
+ const raw = this.currentStdin?.rawMode === true;
25160
+ if (ch === CTRL_C && !raw) {
24580
25161
  this.session.proc.deliver("SIGINT");
24581
25162
  this.write("^C\r\n");
24582
25163
  return;
24583
25164
  }
24584
- if (ch === CTRL_D) {
25165
+ if (ch === CTRL_D && !raw) {
24585
25166
  this.currentStdin?.end();
24586
25167
  return;
24587
25168
  }
24588
- if (!this.currentStdin?.rawMode) this.write(ch === "\r" ? "\r\n" : ch);
24589
- this.currentStdin?.write(ch === "\r" ? "\n" : ch);
25169
+ if (!raw) this.write(ch === "\r" ? "\r\n" : ch);
25170
+ this.currentStdin?.write(!raw && ch === "\r" ? "\n" : ch);
24590
25171
  return;
24591
25172
  }
24592
25173
  if (this.escapeBuffer !== "") {