@phpsandbox/sdk 0.1.1 → 0.1.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.
@@ -29,7 +29,7 @@ var PHPSandbox = (() => {
29
29
  var index_exports = {};
30
30
  __export(index_exports, {
31
31
  ApiError: () => ApiError,
32
- Client: () => Client2,
32
+ Client: () => Client,
33
33
  CommandError: () => CommandError,
34
34
  ComposerCredentials: () => ComposerCredentials,
35
35
  ErrorEvent: () => ErrorEvent,
@@ -43,7 +43,7 @@ var PHPSandbox = (() => {
43
43
  NotebookApi: () => NotebookApi,
44
44
  NotebookComposerCredentialApi: () => NotebookComposerCredentialApi,
45
45
  NotebookInitError: () => NotebookInitError,
46
- NotebookInstance: () => NotebookInstance2,
46
+ NotebookInstance: () => NotebookInstance,
47
47
  NotebookMail: () => NotebookMail,
48
48
  NotebookMailApi: () => NotebookMailApi,
49
49
  NotebookPreview: () => NotebookPreview,
@@ -55,6 +55,7 @@ var PHPSandbox = (() => {
55
55
  NotebookUnavailableError: () => NotebookUnavailableError,
56
56
  PHPSandbox: () => PHPSandbox,
57
57
  PHPSandboxError: () => PHPSandboxError,
58
+ ProcessReadable: () => ProcessReadable,
58
59
  ProcessResult: () => ProcessResult,
59
60
  PromiseTimeoutError: () => PromiseTimeoutError,
60
61
  PublicationApi: () => PublicationApi,
@@ -89,6 +90,7 @@ var PHPSandbox = (() => {
89
90
  PublishStreamSsePhaseEventSchema: () => PublishStreamSsePhaseEventSchema,
90
91
  PublishStreamSseResultEventSchema: () => PublishStreamSseResultEventSchema,
91
92
  RateLimitError: () => RateLimitError,
93
+ RuntimeTelemetry: () => RuntimeTelemetry,
92
94
  SendTimeoutError: () => SendTimeoutError,
93
95
  ServerApi: () => ServerApi,
94
96
  ServerInstance: () => ServerInstance,
@@ -97,9 +99,36 @@ var PHPSandbox = (() => {
97
99
  notebookBuiltinServices: () => notebookBuiltinServices,
98
100
  notebookKnownServices: () => notebookKnownServices,
99
101
  once: () => once,
100
- timeout: () => timeout
102
+ timeout: () => timeout,
103
+ watchRuntimeTelemetry: () => watchRuntimeTelemetry
101
104
  });
102
105
 
106
+ // src/errors/index.ts
107
+ var PromiseTimeoutError = class extends Error {
108
+ constructor(message, time3) {
109
+ super(message);
110
+ this.time = time3;
111
+ }
112
+ };
113
+ var SendTimeoutError = class _SendTimeoutError extends PromiseTimeoutError {
114
+ static fromPromiseTimeoutError(error51) {
115
+ return new _SendTimeoutError(error51.message, error51.time);
116
+ }
117
+ };
118
+ var NotebookUnavailableError = class extends Error {
119
+ constructor(message = "Notebook is no longer available", raw = {}) {
120
+ super(message);
121
+ this.raw = raw;
122
+ this.name = "NotebookUnavailableError";
123
+ }
124
+ };
125
+ var UnsupportedRuntimeTransportError = class extends Error {
126
+ constructor(message) {
127
+ super(message);
128
+ this.name = "UnsupportedRuntimeTransportError";
129
+ }
130
+ };
131
+
103
132
  // src/types.ts
104
133
  var ErrorEvent = class extends Error {
105
134
  constructor(code, message, raw = {}) {
@@ -197,29 +226,194 @@ var PHPSandbox = (() => {
197
226
  }
198
227
  });
199
228
  }
200
- info(path) {
201
- return this.okra.invoke("fs.info", { path });
229
+ async write(path, contents, options = {}) {
230
+ const resolvedOptions = {
231
+ overwrite: true,
232
+ create: true,
233
+ unlock: false,
234
+ atomic: false,
235
+ ...options
236
+ };
237
+ const source = typeof contents === "string" ? new TextEncoder().encode(contents) : contents;
238
+ if (this.okra.runtimeTransport === "rest") {
239
+ const bytes = source instanceof Uint8Array ? source : await collectBytes(source);
240
+ await this.okra.invoke("fs.writeFile", {
241
+ path,
242
+ contents: bytes,
243
+ options: resolvedOptions
244
+ }).catch((error51) => this.handleError(error51));
245
+ return;
246
+ }
247
+ const id = nanoid();
248
+ const completion = this.okra.invoke("fs.writeStream", { id, path, options: resolvedOptions });
249
+ void completion.catch(() => {
250
+ });
251
+ try {
252
+ for await (const chunk of toAsyncIterable(source)) {
253
+ const written = await this.okra.invoke("fs.writeChunk", { id, contents: chunk });
254
+ if (!written) {
255
+ throw new Error(`File write stream ${id} is not available`);
256
+ }
257
+ }
258
+ const ended = await this.okra.invoke("fs.endWrite", { id });
259
+ if (!ended) {
260
+ throw new Error(`File write stream ${id} could not be completed`);
261
+ }
262
+ await completion;
263
+ } catch (error51) {
264
+ await this.okra.invoke("fs.abortWrite", { id }).catch(() => false);
265
+ await completion.catch(() => false);
266
+ throw error51;
267
+ }
202
268
  }
203
- async write(path, contents, options = { overwrite: true, create: true, unlock: false, atomic: false }) {
204
- if (typeof contents === "string") {
205
- return this.okra.invoke("fs.write", { path, contents });
269
+ async read(path) {
270
+ try {
271
+ const content = await this.okra.invoke("fs.readFile", { path });
272
+ return content instanceof Uint8Array ? content : new TextEncoder().encode(content);
273
+ } catch (error51) {
274
+ this.handleError(error51);
275
+ }
276
+ }
277
+ async readText(path) {
278
+ return new TextDecoder().decode(await this.read(path));
279
+ }
280
+ async readLines(path, range) {
281
+ try {
282
+ const result = await this.okra.invoke("fs.readFile", {
283
+ path,
284
+ lineRange: {
285
+ lineStart: range.start,
286
+ lineEnd: range.end
287
+ }
288
+ });
289
+ if (typeof result === "string" || result instanceof Uint8Array) {
290
+ throw new Error(`Unexpected ranged file response for ${path}`);
291
+ }
292
+ return {
293
+ start: result.lineStart,
294
+ end: result.lineEnd,
295
+ content: result.content,
296
+ ...result.error === null ? {} : { warning: result.error }
297
+ };
298
+ } catch (error51) {
299
+ this.handleError(error51);
206
300
  }
207
- const bytes = contents instanceof Uint8Array ? contents : await collectBytes(contents);
208
- await this.writeFile(path, bytes, options);
209
- return true;
210
301
  }
211
- read(path) {
302
+ readStream(path) {
303
+ if (this.okra.runtimeTransport === "rest") {
304
+ return new ReadableStream({
305
+ start: async (controller) => {
306
+ try {
307
+ controller.enqueue(await this.read(path));
308
+ controller.close();
309
+ } catch (error51) {
310
+ controller.error(error51);
311
+ }
312
+ }
313
+ });
314
+ }
315
+ const id = nanoid();
316
+ let disposable = null;
317
+ let closed = false;
212
318
  return new ReadableStream({
213
- start: async (controller) => {
214
- try {
215
- controller.enqueue(await this.readFile(path));
216
- controller.close();
217
- } catch (error51) {
218
- controller.error(error51);
319
+ start: (controller) => {
320
+ disposable = this.okra.listen(`fs.read.${id}`, (event) => {
321
+ controller.enqueue(event.data);
322
+ });
323
+ void this.okra.invoke("fs.readStream", { id, path }).then(() => {
324
+ if (!closed) {
325
+ closed = true;
326
+ disposable?.dispose();
327
+ controller.close();
328
+ }
329
+ }).catch((error51) => {
330
+ if (!closed) {
331
+ closed = true;
332
+ disposable?.dispose();
333
+ controller.error(error51);
334
+ }
335
+ });
336
+ },
337
+ cancel: async () => {
338
+ if (!closed) {
339
+ closed = true;
340
+ disposable?.dispose();
341
+ await this.okra.invoke("fs.cancelRead", { id }).catch(() => false);
219
342
  }
220
343
  }
221
344
  });
222
345
  }
346
+ follow(path, options = {}) {
347
+ if (this.okra.runtimeTransport === "rest") {
348
+ return new ReadableStream({
349
+ start(controller) {
350
+ controller.error(new UnsupportedRuntimeTransportError("Following files requires the realtime transport."));
351
+ }
352
+ });
353
+ }
354
+ const id = nanoid();
355
+ let disposable = null;
356
+ let abort = null;
357
+ let started = false;
358
+ let closed = false;
359
+ const stop = async () => {
360
+ if (closed) {
361
+ return;
362
+ }
363
+ closed = true;
364
+ disposable?.dispose();
365
+ disposable = null;
366
+ if (abort && options.signal) {
367
+ options.signal.removeEventListener("abort", abort);
368
+ }
369
+ abort = null;
370
+ if (started) {
371
+ await this.okra.invoke("fs.cancelFollow", { id }).catch(() => false);
372
+ }
373
+ };
374
+ return new ReadableStream({
375
+ start: (controller) => {
376
+ disposable = this.okra.listen(`fs.follow.${id}`, (event) => {
377
+ if (!closed) {
378
+ controller.enqueue(event.data);
379
+ }
380
+ });
381
+ if (options.signal) {
382
+ abort = () => {
383
+ void stop().finally(() => controller.error(options.signal?.reason ?? new Error("Aborted")));
384
+ };
385
+ if (options.signal.aborted) {
386
+ abort();
387
+ return;
388
+ }
389
+ options.signal.addEventListener("abort", abort, { once: true });
390
+ }
391
+ started = true;
392
+ void this.okra.invoke("fs.follow", { id, path, lines: options.lines ?? 10 }).then(() => {
393
+ if (!closed) {
394
+ closed = true;
395
+ disposable?.dispose();
396
+ if (abort && options.signal) {
397
+ options.signal.removeEventListener("abort", abort);
398
+ }
399
+ abort = null;
400
+ controller.close();
401
+ }
402
+ }).catch((error51) => {
403
+ if (!closed) {
404
+ closed = true;
405
+ disposable?.dispose();
406
+ if (abort && options.signal) {
407
+ options.signal.removeEventListener("abort", abort);
408
+ }
409
+ abort = null;
410
+ controller.error(error51);
411
+ }
412
+ });
413
+ },
414
+ cancel: stop
415
+ });
416
+ }
223
417
  find(query, options = {
224
418
  useIgnoreFiles: false,
225
419
  followSymlinks: false,
@@ -297,53 +491,38 @@ var PHPSandbox = (() => {
297
491
  const disposable = this.okra.listen(`fs.text.search.${sid}`, localOnMatch);
298
492
  return this.okra.invoke("fs.textSearch", { query, options: { ...defaultOptions, ...options } }).finally(() => disposable.dispose());
299
493
  }
300
- mkdir(path) {
301
- return this.okra.invoke("fs.mkdir", { path });
302
- }
303
- move(from, to) {
304
- return this.okra.invoke("fs.move", { from, to });
305
- }
306
- remove(path, type) {
307
- return this.okra.invoke("fs.remove", { path, type });
308
- }
309
- async readFile(path, lineRange) {
310
- try {
311
- const content = await this.okra.invoke("fs.readFile", { path, lineRange });
312
- if (content instanceof Uint8Array) {
313
- return content;
314
- }
315
- if (typeof content === "string") {
316
- return new TextEncoder().encode(content);
317
- }
318
- return content;
319
- } catch (e) {
320
- this.handleError(e);
321
- }
322
- }
323
- tail(path, lines = 10) {
324
- return this.okra.invoke("fs.tail", { path, lines }).catch((e) => this.handleError(e));
325
- }
326
- writeFile(path, contents, options) {
327
- return this.okra.invoke("fs.writeFile", {
328
- path,
329
- contents,
330
- options
331
- }).catch((e) => this.handleError(e));
494
+ tail(path, options = {}) {
495
+ return this.okra.invoke("fs.tail", { path, lines: options.lines ?? 10 }).catch((e) => this.handleError(e));
332
496
  }
333
497
  stat(path) {
334
498
  return this.okra.invoke("fs.stat", { path }).catch((e) => this.handleError(e));
335
499
  }
336
- rename(from, to, options) {
337
- return this.okra.invoke("fs.rename", { from, to, options }).catch((e) => this.handleError(e));
500
+ move(from, to, options = {}) {
501
+ return this.okra.invoke("fs.rename", {
502
+ from,
503
+ to,
504
+ options: { overwrite: false, ...options }
505
+ }).catch((e) => this.handleError(e));
338
506
  }
339
- delete(path, options) {
340
- return this.okra.invoke("fs.delete", { path, options }).catch((e) => this.handleError(e));
507
+ remove(path, options = {}) {
508
+ return this.okra.invoke("fs.delete", {
509
+ path,
510
+ options: { recursive: false, useTrash: false, atomic: false, ...options }
511
+ }).catch((e) => this.handleError(e));
341
512
  }
342
- copy(source, destination, options) {
343
- return this.okra.invoke("fs.copy", { source, destination, options }).catch((e) => this.handleError(e));
513
+ copy(source, destination, options = {}) {
514
+ return this.okra.invoke("fs.copy", {
515
+ source,
516
+ destination,
517
+ options: { overwrite: false, ...options }
518
+ }).catch((e) => this.handleError(e));
344
519
  }
345
- readDirectory(path, include = [], exclude = []) {
346
- return this.okra.invoke("fs.readDirectory", { path, include, exclude }).catch((e) => this.handleError(e));
520
+ list(path, options = {}) {
521
+ return this.okra.invoke("fs.readDirectory", {
522
+ path,
523
+ include: options.include ?? [],
524
+ exclude: options.exclude ?? []
525
+ }).catch((e) => this.handleError(e));
347
526
  }
348
527
  createDirectory(path) {
349
528
  return this.okra.invoke("fs.createDirectory", { path }).catch((e) => this.handleError(e));
@@ -372,17 +551,17 @@ var PHPSandbox = (() => {
372
551
  exists(path) {
373
552
  return this.stat(path).then(() => true).catch(() => false);
374
553
  }
375
- async download(chunk, exclude, include) {
554
+ async download(options = {}) {
376
555
  const id = nanoid();
377
- const stream = !!chunk;
556
+ const stream = !!options.onChunk;
378
557
  const chunks = [];
379
558
  const disposable = this.okra.listen(
380
559
  `fs.download.${id}`,
381
- stream ? chunk : (data) => {
560
+ stream ? options.onChunk : (data) => {
382
561
  chunks.push(data);
383
562
  }
384
563
  );
385
- return this.okra.invoke("fs.download", { id, exclude, include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
564
+ return this.okra.invoke("fs.download", { id, exclude: options.exclude, include: options.include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
386
565
  }
387
566
  tree(path = "/") {
388
567
  return this.okra.invoke("fs.tree", { path });
@@ -394,6 +573,13 @@ var PHPSandbox = (() => {
394
573
  throw e;
395
574
  }
396
575
  };
576
+ async function* toAsyncIterable(contents) {
577
+ if (contents instanceof Uint8Array) {
578
+ yield contents;
579
+ return;
580
+ }
581
+ yield* contents;
582
+ }
397
583
  async function collectBytes(stream) {
398
584
  const chunks = [];
399
585
  let length = 0;
@@ -410,32 +596,6 @@ var PHPSandbox = (() => {
410
596
  return output;
411
597
  }
412
598
 
413
- // src/errors/index.ts
414
- var PromiseTimeoutError = class extends Error {
415
- constructor(message, time3) {
416
- super(message);
417
- this.time = time3;
418
- }
419
- };
420
- var SendTimeoutError = class _SendTimeoutError extends PromiseTimeoutError {
421
- static fromPromiseTimeoutError(error51) {
422
- return new _SendTimeoutError(error51.message, error51.time);
423
- }
424
- };
425
- var NotebookUnavailableError = class extends Error {
426
- constructor(message = "Notebook is no longer available", raw = {}) {
427
- super(message);
428
- this.raw = raw;
429
- this.name = "NotebookUnavailableError";
430
- }
431
- };
432
- var UnsupportedRuntimeTransportError = class extends Error {
433
- constructor(message) {
434
- super(message);
435
- this.name = "UnsupportedRuntimeTransportError";
436
- }
437
- };
438
-
439
599
  // src/utils/promise.ts
440
600
  var timeout = (prom, time3) => {
441
601
  let timer;
@@ -454,30 +614,35 @@ var PHPSandbox = (() => {
454
614
  // src/terminal.ts
455
615
  var TERMINAL_INPUT_FLUSH_DELAY_MS = 8;
456
616
  var terminalInputFlushPattern = /[\x00-\x1f\x7f]/;
457
- var Terminal = class {
617
+ var Terminals = class {
458
618
  constructor(okra) {
459
619
  this.okra = okra;
460
620
  }
461
621
  list() {
462
622
  return this.okra.invoke("terminal.list");
463
623
  }
464
- create(input) {
465
- return this.okra.invoke("terminal.create", input);
466
- }
467
- onStarted(handler) {
468
- return this.okra.listen("terminal.started", handler);
469
- }
470
- resize(id, size) {
471
- return this.okra.invoke("terminal.resize", { id, width: size[0], height: size[1] });
472
- }
473
- onOutput(id, handler) {
474
- return this.okra.listen(`terminal.output.${id}`, handler);
475
- }
476
- input(id, input) {
477
- return Promise.resolve(this.okra.send("terminal.input", { id, input }));
478
- }
479
- listen(event, handler) {
480
- return this.okra.listen(event, handler);
624
+ async create(options = {}) {
625
+ const id = options.id ?? nanoid();
626
+ const handle = this.processHandle(id, options.abortSignal);
627
+ const command = typeof options.command === "string" && options.args?.length ? [options.command, ...options.args] : options.command;
628
+ const { abortSignal, args: _args, ...input } = options;
629
+ try {
630
+ const task = await this.okra.invoke(
631
+ "terminal.create",
632
+ {
633
+ ...input,
634
+ id,
635
+ kind: options.kind ?? "bash",
636
+ size: options.size ?? [80, 24],
637
+ command
638
+ },
639
+ { abortSignal }
640
+ );
641
+ return { ...handle.process, ...task };
642
+ } catch (error51) {
643
+ handle.dispose();
644
+ throw error51;
645
+ }
481
646
  }
482
647
  async attach(id, opts) {
483
648
  const handle = this.processHandle(id, opts?.abortSignal);
@@ -503,31 +668,6 @@ var PHPSandbox = (() => {
503
668
  throw error51;
504
669
  }
505
670
  }
506
- async spawn(command, args, opts) {
507
- const id = opts?.id || nanoid();
508
- const handle = this.processHandle(id, opts?.abortSignal);
509
- const { abortSignal, ...otherOpts } = opts ?? {};
510
- try {
511
- const result = await this.okra.invoke(
512
- "terminal.spawn",
513
- {
514
- command: [command, ...args],
515
- opts: {
516
- id,
517
- ...otherOpts
518
- }
519
- },
520
- { abortSignal }
521
- );
522
- return {
523
- ...handle.process,
524
- ...result
525
- };
526
- } catch (error51) {
527
- handle.dispose();
528
- throw error51;
529
- }
530
- }
531
671
  processHandle(id, abortSignal) {
532
672
  const disposables = /* @__PURE__ */ new Set();
533
673
  let abortHandler;
@@ -553,7 +693,7 @@ var PHPSandbox = (() => {
553
693
  }
554
694
  const input2 = pendingInput;
555
695
  pendingInput = "";
556
- void this.input(id, input2);
696
+ void this.okra.send("terminal.input", { id, input: input2 });
557
697
  };
558
698
  const scheduleInputFlush = () => {
559
699
  if (inputFlushTimer) {
@@ -561,7 +701,8 @@ var PHPSandbox = (() => {
561
701
  }
562
702
  inputFlushTimer = setTimeout(flushInput, TERMINAL_INPUT_FLUSH_DELAY_MS);
563
703
  };
564
- const queueInput = (chunk) => {
704
+ const queueInput = (value) => {
705
+ const chunk = typeof value === "string" ? value : new TextDecoder().decode(value);
565
706
  pendingInput += chunk;
566
707
  if (terminalInputFlushPattern.test(chunk)) {
567
708
  flushInput();
@@ -583,7 +724,7 @@ var PHPSandbox = (() => {
583
724
  start: (_controller) => {
584
725
  controller = _controller;
585
726
  disposables.add(
586
- this.listen(`terminal.output.${id}`, (data) => {
727
+ this.okra.listen(`terminal.output.${id}`, (data) => {
587
728
  controller?.enqueue(data.output);
588
729
  })
589
730
  );
@@ -595,7 +736,7 @@ var PHPSandbox = (() => {
595
736
  });
596
737
  const completion = new Promise((resolve) => {
597
738
  disposables.add(
598
- this.listen(`terminal.close.${id}`, (data) => {
739
+ this.okra.listen(`terminal.close.${id}`, (data) => {
599
740
  if (controller) {
600
741
  try {
601
742
  controller.close();
@@ -660,37 +801,9 @@ var PHPSandbox = (() => {
660
801
  state() {
661
802
  return this.okra.invoke("container.state");
662
803
  }
663
- openedPorts() {
664
- return this.okra.invoke("container.opened-ports");
665
- }
666
804
  setPhp(version2) {
667
805
  return this.okra.invoke("container.set-php", { version: version2 });
668
806
  }
669
- enableTelemetry(features) {
670
- return this.okra.invoke("container.stream-telemetry", { features: Array.from(features) });
671
- }
672
- stopTelemetry() {
673
- return this.okra.invoke("container.stop-telemetry");
674
- }
675
- listen(event, handler) {
676
- return this.okra.listen(event, handler);
677
- }
678
- onPort(handler) {
679
- let ports = [];
680
- return this.listen("container.ports", (newPorts) => {
681
- for (const port of newPorts) {
682
- if (!ports.some((p) => p.port === port.port)) {
683
- handler(port, "open");
684
- }
685
- }
686
- for (const port of ports) {
687
- if (!newPorts.some((p) => p.port === port.port)) {
688
- handler(port, "close");
689
- }
690
- }
691
- ports = newPorts;
692
- });
693
- }
694
807
  };
695
808
 
696
809
  // src/auth.ts
@@ -777,76 +890,6 @@ var PHPSandbox = (() => {
777
890
  }
778
891
  };
779
892
 
780
- // src/composer.ts
781
- var Composer = class {
782
- constructor(okra, client) {
783
- this.okra = okra;
784
- this.credentials = new ComposerCredentials(client, okra.data.id);
785
- }
786
- invoke(command, args = {}, options = {}) {
787
- return this.okra.invoke("composer.invoke", { command, args, options });
788
- }
789
- dumpAutoload() {
790
- return this.okra.invoke("composer.dump-autoload");
791
- }
792
- install(options = {}) {
793
- return this.invoke("install", {}, options);
794
- }
795
- update(args, options = {}) {
796
- return this.invoke("update", args, options);
797
- }
798
- require(packages, options = {}) {
799
- return this.invoke("require", packages, options);
800
- }
801
- remove(args, options = {}) {
802
- return this.invoke("remove", args, options);
803
- }
804
- stream(fn) {
805
- return this.okra.listen("composer.log", fn);
806
- }
807
- packages() {
808
- return this.okra.invoke("composer.packages");
809
- }
810
- };
811
-
812
- // src/log.ts
813
- var Log = class {
814
- constructor(okra) {
815
- this.okra = okra;
816
- }
817
- stream(fn) {
818
- return this.okra.listen("notebook.log", fn);
819
- }
820
- listen(event, handler) {
821
- return this.okra.listen(event, handler);
822
- }
823
- };
824
-
825
- // src/repl.ts
826
- var Repl = class {
827
- constructor(okra) {
828
- this.okra = okra;
829
- }
830
- eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
831
- return this.okra.invoke("repl.eval", { code, args, repl });
832
- }
833
- write(input) {
834
- return this.okra.invoke("repl.write", { input });
835
- }
836
- stop() {
837
- return this.okra.invoke("repl.stop");
838
- }
839
- resize(cols, rows) {
840
- return this.okra.invoke("repl.resize", { cols, rows });
841
- }
842
- listen(event, handler) {
843
- return this.okra.listen(event, handler);
844
- }
845
- onOutput(handler) {
846
- return this.okra.listen("repl.output", handler);
847
- }
848
- };
849
-
850
893
  // src/shell.ts
851
894
  function resolveRunInput(command, argsOrOpts = [], opts) {
852
895
  const args = Array.isArray(argsOrOpts) ? argsOrOpts : [];
@@ -876,7 +919,7 @@ var PHPSandbox = (() => {
876
919
  return this;
877
920
  }
878
921
  };
879
- var OutputStream = class {
922
+ var ProcessReadable = class {
880
923
  constructor() {
881
924
  this.controller = null;
882
925
  this.closed = false;
@@ -916,9 +959,9 @@ var PHPSandbox = (() => {
916
959
  constructor(id, okra, command, opts) {
917
960
  this.id = id;
918
961
  this.okra = okra;
919
- const output = new OutputStream();
920
- const stdout = new OutputStream();
921
- const stderr = new OutputStream();
962
+ const output = new ProcessReadable();
963
+ const stdout = new ProcessReadable();
964
+ const stderr = new ProcessReadable();
922
965
  this.output = output.stream;
923
966
  this.stdout = stdout.stream;
924
967
  this.stderr = stderr.stream;
@@ -987,6 +1030,264 @@ var PHPSandbox = (() => {
987
1030
  }
988
1031
  };
989
1032
 
1033
+ // src/composer.ts
1034
+ var ComposerProcess = class {
1035
+ constructor(id, okra, command, args, options) {
1036
+ this.id = id;
1037
+ this.okra = okra;
1038
+ const output = new ProcessReadable();
1039
+ const stdout = new ProcessReadable();
1040
+ const stderr = new ProcessReadable();
1041
+ this.output = output.stream;
1042
+ this.stdout = stdout.stream;
1043
+ this.stderr = stderr.stream;
1044
+ const encoder = new TextEncoder();
1045
+ let subscription = this.okra.listen(`composer.output.${this.id}`, (event) => {
1046
+ const data = encoder.encode(event.output);
1047
+ const source = event.source ?? "output";
1048
+ output.enqueue({ source, data });
1049
+ if (source === "stderr") {
1050
+ stderr.enqueue(data);
1051
+ } else {
1052
+ stdout.enqueue(data);
1053
+ }
1054
+ });
1055
+ const finish = () => {
1056
+ subscription?.dispose();
1057
+ subscription = null;
1058
+ output.close();
1059
+ stdout.close();
1060
+ stderr.close();
1061
+ };
1062
+ const fail = (error51) => {
1063
+ subscription?.dispose();
1064
+ subscription = null;
1065
+ output.fail(error51);
1066
+ stdout.fail(error51);
1067
+ stderr.fail(error51);
1068
+ };
1069
+ this.completion = this.okra.invoke("composer.invoke", { id, command, args, options }).then((result) => {
1070
+ finish();
1071
+ return new ProcessResult(result.output, result.stdout, result.stderr, result.exitCode);
1072
+ }).catch((error51) => {
1073
+ fail(error51);
1074
+ throw error51;
1075
+ });
1076
+ }
1077
+ wait() {
1078
+ return this.completion;
1079
+ }
1080
+ async kill() {
1081
+ await this.okra.invoke("composer.kill", { id: this.id });
1082
+ }
1083
+ };
1084
+ var Composer = class {
1085
+ constructor(okra, client) {
1086
+ this.okra = okra;
1087
+ this.credentials = new ComposerCredentials(client, okra.data.id);
1088
+ }
1089
+ run(command, args = {}, options = {}) {
1090
+ return new ComposerProcess(nanoid(), this.okra, command, args, options);
1091
+ }
1092
+ dumpAutoload(options = {}) {
1093
+ return this.run("dump-autoload", {}, options);
1094
+ }
1095
+ install(options = {}) {
1096
+ return this.run("install", {}, options);
1097
+ }
1098
+ update(args, options = {}) {
1099
+ return this.run("update", args, options);
1100
+ }
1101
+ require(packages, options = {}) {
1102
+ return this.run("require", packages, options);
1103
+ }
1104
+ remove(args, options = {}) {
1105
+ return this.run("remove", args, options);
1106
+ }
1107
+ packages() {
1108
+ return this.okra.invoke("composer.packages");
1109
+ }
1110
+ };
1111
+
1112
+ // src/runtime-telemetry.ts
1113
+ var RuntimeTelemetry = class {
1114
+ constructor(okra) {
1115
+ this.okra = okra;
1116
+ this.references = /* @__PURE__ */ new Map();
1117
+ this.synchronization = Promise.resolve();
1118
+ if (okra.runtimeTransport === "realtime") {
1119
+ okra.onDidConnect(() => {
1120
+ if (this.references.size > 0) {
1121
+ void this.synchronize();
1122
+ }
1123
+ });
1124
+ }
1125
+ }
1126
+ async acquire(feature) {
1127
+ this.references.set(feature, (this.references.get(feature) ?? 0) + 1);
1128
+ try {
1129
+ await this.synchronize();
1130
+ } catch (error51) {
1131
+ this.removeReference(feature);
1132
+ throw error51;
1133
+ }
1134
+ let released = false;
1135
+ return async () => {
1136
+ if (released) {
1137
+ return;
1138
+ }
1139
+ released = true;
1140
+ this.removeReference(feature);
1141
+ await this.synchronize();
1142
+ };
1143
+ }
1144
+ removeReference(feature) {
1145
+ const references = this.references.get(feature) ?? 0;
1146
+ if (references <= 1) {
1147
+ this.references.delete(feature);
1148
+ } else {
1149
+ this.references.set(feature, references - 1);
1150
+ }
1151
+ }
1152
+ synchronize() {
1153
+ this.synchronization = this.synchronization.catch(() => {
1154
+ }).then(async () => {
1155
+ const features = Array.from(this.references.keys());
1156
+ if (features.length === 0) {
1157
+ await this.okra.invoke("container.stop-telemetry");
1158
+ } else {
1159
+ await this.okra.invoke("container.stream-telemetry", { features });
1160
+ }
1161
+ });
1162
+ return this.synchronization;
1163
+ }
1164
+ };
1165
+ function watchRuntimeTelemetry(okra, telemetry, feature, event, transform2, options = {}) {
1166
+ let subscription = null;
1167
+ let release = null;
1168
+ let closed = false;
1169
+ let abort = null;
1170
+ const stop = async () => {
1171
+ if (closed) {
1172
+ return;
1173
+ }
1174
+ closed = true;
1175
+ subscription?.dispose();
1176
+ subscription = null;
1177
+ if (abort && options.signal) {
1178
+ options.signal.removeEventListener("abort", abort);
1179
+ }
1180
+ abort = null;
1181
+ await release?.();
1182
+ release = null;
1183
+ };
1184
+ return new ReadableStream({
1185
+ start(controller) {
1186
+ subscription = okra.listen(event, (data) => {
1187
+ if (!closed) {
1188
+ controller.enqueue(transform2(data));
1189
+ }
1190
+ });
1191
+ if (options.signal) {
1192
+ abort = () => {
1193
+ void stop().finally(() => controller.error(options.signal?.reason ?? new Error("Aborted")));
1194
+ };
1195
+ if (options.signal.aborted) {
1196
+ abort();
1197
+ return;
1198
+ }
1199
+ options.signal.addEventListener("abort", abort, { once: true });
1200
+ }
1201
+ void telemetry.acquire(feature).then(async (releaseTelemetry) => {
1202
+ if (closed) {
1203
+ await releaseTelemetry();
1204
+ return;
1205
+ }
1206
+ release = releaseTelemetry;
1207
+ }).catch((error51) => {
1208
+ if (!closed) {
1209
+ closed = true;
1210
+ subscription?.dispose();
1211
+ if (abort && options.signal) {
1212
+ options.signal.removeEventListener("abort", abort);
1213
+ }
1214
+ abort = null;
1215
+ controller.error(error51);
1216
+ }
1217
+ });
1218
+ },
1219
+ cancel: stop
1220
+ });
1221
+ }
1222
+
1223
+ // src/log.ts
1224
+ var Logs = class {
1225
+ constructor(okra, telemetry) {
1226
+ this.okra = okra;
1227
+ this.telemetry = telemetry;
1228
+ }
1229
+ follow(options = {}) {
1230
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "logs", "notebook.log", (message) => ({
1231
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1232
+ source: "runtime",
1233
+ message
1234
+ }), options);
1235
+ }
1236
+ };
1237
+
1238
+ // src/metrics.ts
1239
+ var Metrics = class {
1240
+ constructor(okra, telemetry) {
1241
+ this.okra = okra;
1242
+ this.telemetry = telemetry;
1243
+ }
1244
+ current() {
1245
+ return this.okra.invoke("container.stats");
1246
+ }
1247
+ watch(options = {}) {
1248
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "stats", "container.stats", (stats) => stats, options);
1249
+ }
1250
+ };
1251
+
1252
+ // src/ports.ts
1253
+ var Ports = class {
1254
+ constructor(okra, telemetry) {
1255
+ this.okra = okra;
1256
+ this.telemetry = telemetry;
1257
+ }
1258
+ list() {
1259
+ return this.okra.invoke("container.opened-ports");
1260
+ }
1261
+ watch(options = {}) {
1262
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "ports", "container.ports", (ports) => ports, options);
1263
+ }
1264
+ };
1265
+
1266
+ // src/repl.ts
1267
+ var Repl = class {
1268
+ constructor(okra) {
1269
+ this.okra = okra;
1270
+ }
1271
+ eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
1272
+ return this.okra.invoke("repl.eval", { code, args, repl });
1273
+ }
1274
+ write(input) {
1275
+ return this.okra.invoke("repl.write", { input });
1276
+ }
1277
+ stop() {
1278
+ return this.okra.invoke("repl.stop");
1279
+ }
1280
+ resize(cols, rows) {
1281
+ return this.okra.invoke("repl.resize", { cols, rows });
1282
+ }
1283
+ listen(event, handler) {
1284
+ return this.okra.listen(event, handler);
1285
+ }
1286
+ onOutput(handler) {
1287
+ return this.okra.listen("repl.output", handler);
1288
+ }
1289
+ };
1290
+
990
1291
  // src/services.ts
991
1292
  var notebookBuiltinServices = ["redis"];
992
1293
  var notebookKnownServices = ["start", "nginx", ...notebookBuiltinServices];
@@ -1006,12 +1307,6 @@ var PHPSandbox = (() => {
1006
1307
  stop(name) {
1007
1308
  return this.okra.invoke("service.stop", { name });
1008
1309
  }
1009
- onLog(id, handler) {
1010
- return this.okra.listen(`service.log.${id}`, handler);
1011
- }
1012
- listen(event, handler) {
1013
- return this.okra.listen(event, handler);
1014
- }
1015
1310
  logs(name, options = {}) {
1016
1311
  if (options.follow) {
1017
1312
  return this.followLogs(name, options);
@@ -1064,11 +1359,11 @@ var PHPSandbox = (() => {
1064
1359
  close();
1065
1360
  return this.okra.invoke("service.stop-logs", { id });
1066
1361
  });
1067
- const output = new ReadableStream({
1362
+ return new ReadableStream({
1068
1363
  start: (_controller) => {
1069
1364
  controller = _controller;
1070
1365
  disposables.add(
1071
- this.onLog(id, (data) => {
1366
+ this.okra.listen(`service.log.${id}`, (data) => {
1072
1367
  controller?.enqueue(data.output);
1073
1368
  })
1074
1369
  );
@@ -1082,10 +1377,9 @@ var PHPSandbox = (() => {
1082
1377
  });
1083
1378
  },
1084
1379
  cancel: () => {
1085
- void stop();
1380
+ return stop().then(() => void 0);
1086
1381
  }
1087
1382
  });
1088
- return { id, output, stop };
1089
1383
  }
1090
1384
  };
1091
1385
 
@@ -19213,6 +19507,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19213
19507
  "container.stop": { method: "POST", path: "/runtime/container/stop" },
19214
19508
  "container.set-php": { method: "PUT", path: "/runtime/container/php" },
19215
19509
  "container.opened-ports": { method: "GET", path: "/runtime/container/ports" },
19510
+ "container.stats": { method: "GET", path: "/runtime/container/stats" },
19216
19511
  "terminal.list": { method: "GET", path: "/runtime/terminals" },
19217
19512
  "shell.exec": { method: "POST", path: "/runtime/executions" },
19218
19513
  "fs.info": { method: "POST", path: "/runtime/files/info" },
@@ -19238,10 +19533,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19238
19533
  "service.logs": { method: "POST", path: "/runtime/services/logs" },
19239
19534
  "composer.invoke": { method: "POST", path: "/runtime/composer/executions" },
19240
19535
  "composer.packages": { method: "GET", path: "/runtime/composer/packages" },
19241
- "composer.dump-autoload": {
19242
- method: "POST",
19243
- path: "/runtime/composer/autoload"
19244
- },
19245
19536
  "git.checkpoint": { method: "POST", path: "/runtime/git/checkpoints" },
19246
19537
  "git.checkout": { method: "POST", path: "/runtime/git/checkouts" },
19247
19538
  "git.log": { method: "POST", path: "/runtime/git/log" },
@@ -19485,7 +19776,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19485
19776
  }
19486
19777
  async create(template, input = {}, init = true) {
19487
19778
  const response = await this.client.post("/notebook", { template, ...input });
19488
- const instance = new NotebookInstance2(response.data, this.client);
19779
+ const instance = new NotebookInstance(response.data, this.client);
19489
19780
  if (!init) {
19490
19781
  return instance;
19491
19782
  }
@@ -19493,25 +19784,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19493
19784
  }
19494
19785
  async get(id) {
19495
19786
  const response = await this.client.get(`/notebook/${id}`);
19496
- return new NotebookInstance2(response.data, this.client);
19787
+ return new NotebookInstance(response.data, this.client);
19497
19788
  }
19498
19789
  async update(id, input = {}) {
19499
19790
  const response = await this.client.put(`/notebook/${id}`, input);
19500
- return new NotebookInstance2(response.data, this.client);
19791
+ return new NotebookInstance(response.data, this.client);
19501
19792
  }
19502
19793
  async destroy(id) {
19503
19794
  await this.client.delete(`/notebook/${id}`);
19504
19795
  }
19505
19796
  async fork(id, input = {}) {
19506
19797
  const response = await this.client.post(`/notebook/${id}/fork`, input);
19507
- return new NotebookInstance2(response.data, this.client);
19798
+ return new NotebookInstance(response.data, this.client);
19508
19799
  }
19509
19800
  async gitCredentials(id) {
19510
19801
  const response = await this.client.post(`/notebook/${id}/git/credentials`);
19511
19802
  return response.data;
19512
19803
  }
19513
19804
  openFromData(data) {
19514
- return this.init(new NotebookInstance2(data, this.client));
19805
+ return this.init(new NotebookInstance(data, this.client));
19515
19806
  }
19516
19807
  async init(instance) {
19517
19808
  await instance.ready();
@@ -19526,7 +19817,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19526
19817
  baseUrl.hash = "";
19527
19818
  return baseUrl.toString();
19528
19819
  }
19529
- var Client2 = class {
19820
+ var Client = class {
19530
19821
  constructor(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19531
19822
  this.runtimeTransportValue = "realtime";
19532
19823
  this.fetch = globalThis.fetch;
@@ -19610,7 +19901,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19610
19901
  return { data: body === "" ? void 0 : JSON.parse(body) };
19611
19902
  }
19612
19903
  };
19613
- var PHPSandbox = class _PHPSandbox extends Client2 {
19904
+ var PHPSandbox = class _PHPSandbox extends Client {
19614
19905
  static realtime(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19615
19906
  return new _PHPSandbox(token, url2, options);
19616
19907
  }
@@ -19718,7 +20009,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19718
20009
  }
19719
20010
  };
19720
20011
  var _initPromise, _NotebookInstance_instances, init_fn;
19721
- var NotebookInstance2 = class {
20012
+ var NotebookInstance = class {
19722
20013
  constructor(data, client) {
19723
20014
  this.data = data;
19724
20015
  this.client = client;
@@ -19753,15 +20044,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19753
20044
  this.watchConnection();
19754
20045
  __privateSet(this, _initPromise, __privateMethod(this, _NotebookInstance_instances, init_fn).call(this));
19755
20046
  }
19756
- this.file = new Filesystem(
20047
+ this.files = new Filesystem(
19757
20048
  this,
19758
20049
  this.socket === null ? null : (handler) => this.onDidConnect(handler)
19759
20050
  );
19760
- this.terminal = new Terminal(this);
20051
+ this.terminals = new Terminals(this);
19761
20052
  this.auth = new Auth(this);
19762
20053
  this.lsp = new Lsp(this);
19763
20054
  this.composer = new Composer(this, client);
19764
- this.log = new Log(this);
20055
+ const telemetry = new RuntimeTelemetry(this);
20056
+ this.logs = new Logs(this, telemetry);
20057
+ this.metrics = new Metrics(this, telemetry);
20058
+ this.ports = new Ports(this, telemetry);
19765
20059
  this.repl = new Repl(this);
19766
20060
  this.container = new Container(this);
19767
20061
  this.shell = new Shell(this);
@@ -19807,6 +20101,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19807
20101
  }
19808
20102
  return this.shell.run(command, argsOrOpts);
19809
20103
  }
20104
+ exec(command, argsOrOpts = [], opts) {
20105
+ if (Array.isArray(argsOrOpts)) {
20106
+ return this.run(command, argsOrOpts, opts).wait();
20107
+ }
20108
+ return this.run(command, argsOrOpts).wait();
20109
+ }
19810
20110
  gitCredentials() {
19811
20111
  return this.client.notebook.gitCredentials(this.data.id);
19812
20112
  }
@@ -19897,9 +20197,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19897
20197
  const socket = this.realtimeSocket();
19898
20198
  socket.onDidConnect(() => {
19899
20199
  socket.emit("okra.connected");
19900
- if (this.client.options.telemetry) {
19901
- this.container.enableTelemetry(this.client.options.telemetry);
19902
- }
19903
20200
  });
19904
20201
  socket.onDidClose(() => {
19905
20202
  socket.emit("okra.disconnected");