@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.
@@ -12,6 +12,32 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
12
12
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
13
13
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
14
14
 
15
+ // src/errors/index.ts
16
+ var PromiseTimeoutError = class extends Error {
17
+ constructor(message, time3) {
18
+ super(message);
19
+ this.time = time3;
20
+ }
21
+ };
22
+ var SendTimeoutError = class _SendTimeoutError extends PromiseTimeoutError {
23
+ static fromPromiseTimeoutError(error51) {
24
+ return new _SendTimeoutError(error51.message, error51.time);
25
+ }
26
+ };
27
+ var NotebookUnavailableError = class extends Error {
28
+ constructor(message = "Notebook is no longer available", raw = {}) {
29
+ super(message);
30
+ this.raw = raw;
31
+ this.name = "NotebookUnavailableError";
32
+ }
33
+ };
34
+ var UnsupportedRuntimeTransportError = class extends Error {
35
+ constructor(message) {
36
+ super(message);
37
+ this.name = "UnsupportedRuntimeTransportError";
38
+ }
39
+ };
40
+
15
41
  // src/types.ts
16
42
  var ErrorEvent = class extends Error {
17
43
  constructor(code, message, raw = {}) {
@@ -109,29 +135,194 @@ var Filesystem = class {
109
135
  }
110
136
  });
111
137
  }
112
- info(path) {
113
- return this.okra.invoke("fs.info", { path });
138
+ async write(path, contents, options = {}) {
139
+ const resolvedOptions = {
140
+ overwrite: true,
141
+ create: true,
142
+ unlock: false,
143
+ atomic: false,
144
+ ...options
145
+ };
146
+ const source = typeof contents === "string" ? new TextEncoder().encode(contents) : contents;
147
+ if (this.okra.runtimeTransport === "rest") {
148
+ const bytes = source instanceof Uint8Array ? source : await collectBytes(source);
149
+ await this.okra.invoke("fs.writeFile", {
150
+ path,
151
+ contents: bytes,
152
+ options: resolvedOptions
153
+ }).catch((error51) => this.handleError(error51));
154
+ return;
155
+ }
156
+ const id = nanoid();
157
+ const completion = this.okra.invoke("fs.writeStream", { id, path, options: resolvedOptions });
158
+ void completion.catch(() => {
159
+ });
160
+ try {
161
+ for await (const chunk of toAsyncIterable(source)) {
162
+ const written = await this.okra.invoke("fs.writeChunk", { id, contents: chunk });
163
+ if (!written) {
164
+ throw new Error(`File write stream ${id} is not available`);
165
+ }
166
+ }
167
+ const ended = await this.okra.invoke("fs.endWrite", { id });
168
+ if (!ended) {
169
+ throw new Error(`File write stream ${id} could not be completed`);
170
+ }
171
+ await completion;
172
+ } catch (error51) {
173
+ await this.okra.invoke("fs.abortWrite", { id }).catch(() => false);
174
+ await completion.catch(() => false);
175
+ throw error51;
176
+ }
114
177
  }
115
- async write(path, contents, options = { overwrite: true, create: true, unlock: false, atomic: false }) {
116
- if (typeof contents === "string") {
117
- return this.okra.invoke("fs.write", { path, contents });
178
+ async read(path) {
179
+ try {
180
+ const content = await this.okra.invoke("fs.readFile", { path });
181
+ return content instanceof Uint8Array ? content : new TextEncoder().encode(content);
182
+ } catch (error51) {
183
+ this.handleError(error51);
184
+ }
185
+ }
186
+ async readText(path) {
187
+ return new TextDecoder().decode(await this.read(path));
188
+ }
189
+ async readLines(path, range) {
190
+ try {
191
+ const result = await this.okra.invoke("fs.readFile", {
192
+ path,
193
+ lineRange: {
194
+ lineStart: range.start,
195
+ lineEnd: range.end
196
+ }
197
+ });
198
+ if (typeof result === "string" || result instanceof Uint8Array) {
199
+ throw new Error(`Unexpected ranged file response for ${path}`);
200
+ }
201
+ return {
202
+ start: result.lineStart,
203
+ end: result.lineEnd,
204
+ content: result.content,
205
+ ...result.error === null ? {} : { warning: result.error }
206
+ };
207
+ } catch (error51) {
208
+ this.handleError(error51);
118
209
  }
119
- const bytes = contents instanceof Uint8Array ? contents : await collectBytes(contents);
120
- await this.writeFile(path, bytes, options);
121
- return true;
122
210
  }
123
- read(path) {
211
+ readStream(path) {
212
+ if (this.okra.runtimeTransport === "rest") {
213
+ return new ReadableStream({
214
+ start: async (controller) => {
215
+ try {
216
+ controller.enqueue(await this.read(path));
217
+ controller.close();
218
+ } catch (error51) {
219
+ controller.error(error51);
220
+ }
221
+ }
222
+ });
223
+ }
224
+ const id = nanoid();
225
+ let disposable = null;
226
+ let closed = false;
124
227
  return new ReadableStream({
125
- start: async (controller) => {
126
- try {
127
- controller.enqueue(await this.readFile(path));
128
- controller.close();
129
- } catch (error51) {
130
- controller.error(error51);
228
+ start: (controller) => {
229
+ disposable = this.okra.listen(`fs.read.${id}`, (event) => {
230
+ controller.enqueue(event.data);
231
+ });
232
+ void this.okra.invoke("fs.readStream", { id, path }).then(() => {
233
+ if (!closed) {
234
+ closed = true;
235
+ disposable?.dispose();
236
+ controller.close();
237
+ }
238
+ }).catch((error51) => {
239
+ if (!closed) {
240
+ closed = true;
241
+ disposable?.dispose();
242
+ controller.error(error51);
243
+ }
244
+ });
245
+ },
246
+ cancel: async () => {
247
+ if (!closed) {
248
+ closed = true;
249
+ disposable?.dispose();
250
+ await this.okra.invoke("fs.cancelRead", { id }).catch(() => false);
131
251
  }
132
252
  }
133
253
  });
134
254
  }
255
+ follow(path, options = {}) {
256
+ if (this.okra.runtimeTransport === "rest") {
257
+ return new ReadableStream({
258
+ start(controller) {
259
+ controller.error(new UnsupportedRuntimeTransportError("Following files requires the realtime transport."));
260
+ }
261
+ });
262
+ }
263
+ const id = nanoid();
264
+ let disposable = null;
265
+ let abort = null;
266
+ let started = false;
267
+ let closed = false;
268
+ const stop = async () => {
269
+ if (closed) {
270
+ return;
271
+ }
272
+ closed = true;
273
+ disposable?.dispose();
274
+ disposable = null;
275
+ if (abort && options.signal) {
276
+ options.signal.removeEventListener("abort", abort);
277
+ }
278
+ abort = null;
279
+ if (started) {
280
+ await this.okra.invoke("fs.cancelFollow", { id }).catch(() => false);
281
+ }
282
+ };
283
+ return new ReadableStream({
284
+ start: (controller) => {
285
+ disposable = this.okra.listen(`fs.follow.${id}`, (event) => {
286
+ if (!closed) {
287
+ controller.enqueue(event.data);
288
+ }
289
+ });
290
+ if (options.signal) {
291
+ abort = () => {
292
+ void stop().finally(() => controller.error(options.signal?.reason ?? new Error("Aborted")));
293
+ };
294
+ if (options.signal.aborted) {
295
+ abort();
296
+ return;
297
+ }
298
+ options.signal.addEventListener("abort", abort, { once: true });
299
+ }
300
+ started = true;
301
+ void this.okra.invoke("fs.follow", { id, path, lines: options.lines ?? 10 }).then(() => {
302
+ if (!closed) {
303
+ closed = true;
304
+ disposable?.dispose();
305
+ if (abort && options.signal) {
306
+ options.signal.removeEventListener("abort", abort);
307
+ }
308
+ abort = null;
309
+ controller.close();
310
+ }
311
+ }).catch((error51) => {
312
+ if (!closed) {
313
+ closed = true;
314
+ disposable?.dispose();
315
+ if (abort && options.signal) {
316
+ options.signal.removeEventListener("abort", abort);
317
+ }
318
+ abort = null;
319
+ controller.error(error51);
320
+ }
321
+ });
322
+ },
323
+ cancel: stop
324
+ });
325
+ }
135
326
  find(query, options = {
136
327
  useIgnoreFiles: false,
137
328
  followSymlinks: false,
@@ -209,53 +400,38 @@ var Filesystem = class {
209
400
  const disposable = this.okra.listen(`fs.text.search.${sid}`, localOnMatch);
210
401
  return this.okra.invoke("fs.textSearch", { query, options: { ...defaultOptions, ...options } }).finally(() => disposable.dispose());
211
402
  }
212
- mkdir(path) {
213
- return this.okra.invoke("fs.mkdir", { path });
214
- }
215
- move(from, to) {
216
- return this.okra.invoke("fs.move", { from, to });
217
- }
218
- remove(path, type) {
219
- return this.okra.invoke("fs.remove", { path, type });
220
- }
221
- async readFile(path, lineRange) {
222
- try {
223
- const content = await this.okra.invoke("fs.readFile", { path, lineRange });
224
- if (content instanceof Uint8Array) {
225
- return content;
226
- }
227
- if (typeof content === "string") {
228
- return new TextEncoder().encode(content);
229
- }
230
- return content;
231
- } catch (e) {
232
- this.handleError(e);
233
- }
234
- }
235
- tail(path, lines = 10) {
236
- return this.okra.invoke("fs.tail", { path, lines }).catch((e) => this.handleError(e));
237
- }
238
- writeFile(path, contents, options) {
239
- return this.okra.invoke("fs.writeFile", {
240
- path,
241
- contents,
242
- options
243
- }).catch((e) => this.handleError(e));
403
+ tail(path, options = {}) {
404
+ return this.okra.invoke("fs.tail", { path, lines: options.lines ?? 10 }).catch((e) => this.handleError(e));
244
405
  }
245
406
  stat(path) {
246
407
  return this.okra.invoke("fs.stat", { path }).catch((e) => this.handleError(e));
247
408
  }
248
- rename(from, to, options) {
249
- return this.okra.invoke("fs.rename", { from, to, options }).catch((e) => this.handleError(e));
409
+ move(from, to, options = {}) {
410
+ return this.okra.invoke("fs.rename", {
411
+ from,
412
+ to,
413
+ options: { overwrite: false, ...options }
414
+ }).catch((e) => this.handleError(e));
250
415
  }
251
- delete(path, options) {
252
- return this.okra.invoke("fs.delete", { path, options }).catch((e) => this.handleError(e));
416
+ remove(path, options = {}) {
417
+ return this.okra.invoke("fs.delete", {
418
+ path,
419
+ options: { recursive: false, useTrash: false, atomic: false, ...options }
420
+ }).catch((e) => this.handleError(e));
253
421
  }
254
- copy(source, destination, options) {
255
- return this.okra.invoke("fs.copy", { source, destination, options }).catch((e) => this.handleError(e));
422
+ copy(source, destination, options = {}) {
423
+ return this.okra.invoke("fs.copy", {
424
+ source,
425
+ destination,
426
+ options: { overwrite: false, ...options }
427
+ }).catch((e) => this.handleError(e));
256
428
  }
257
- readDirectory(path, include = [], exclude = []) {
258
- return this.okra.invoke("fs.readDirectory", { path, include, exclude }).catch((e) => this.handleError(e));
429
+ list(path, options = {}) {
430
+ return this.okra.invoke("fs.readDirectory", {
431
+ path,
432
+ include: options.include ?? [],
433
+ exclude: options.exclude ?? []
434
+ }).catch((e) => this.handleError(e));
259
435
  }
260
436
  createDirectory(path) {
261
437
  return this.okra.invoke("fs.createDirectory", { path }).catch((e) => this.handleError(e));
@@ -284,17 +460,17 @@ var Filesystem = class {
284
460
  exists(path) {
285
461
  return this.stat(path).then(() => true).catch(() => false);
286
462
  }
287
- async download(chunk, exclude, include) {
463
+ async download(options = {}) {
288
464
  const id = nanoid();
289
- const stream = !!chunk;
465
+ const stream = !!options.onChunk;
290
466
  const chunks = [];
291
467
  const disposable = this.okra.listen(
292
468
  `fs.download.${id}`,
293
- stream ? chunk : (data) => {
469
+ stream ? options.onChunk : (data) => {
294
470
  chunks.push(data);
295
471
  }
296
472
  );
297
- return this.okra.invoke("fs.download", { id, exclude, include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
473
+ return this.okra.invoke("fs.download", { id, exclude: options.exclude, include: options.include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
298
474
  }
299
475
  tree(path = "/") {
300
476
  return this.okra.invoke("fs.tree", { path });
@@ -306,6 +482,13 @@ var Filesystem = class {
306
482
  throw e;
307
483
  }
308
484
  };
485
+ async function* toAsyncIterable(contents) {
486
+ if (contents instanceof Uint8Array) {
487
+ yield contents;
488
+ return;
489
+ }
490
+ yield* contents;
491
+ }
309
492
  async function collectBytes(stream) {
310
493
  const chunks = [];
311
494
  let length = 0;
@@ -322,32 +505,6 @@ async function collectBytes(stream) {
322
505
  return output;
323
506
  }
324
507
 
325
- // src/errors/index.ts
326
- var PromiseTimeoutError = class extends Error {
327
- constructor(message, time3) {
328
- super(message);
329
- this.time = time3;
330
- }
331
- };
332
- var SendTimeoutError = class _SendTimeoutError extends PromiseTimeoutError {
333
- static fromPromiseTimeoutError(error51) {
334
- return new _SendTimeoutError(error51.message, error51.time);
335
- }
336
- };
337
- var NotebookUnavailableError = class extends Error {
338
- constructor(message = "Notebook is no longer available", raw = {}) {
339
- super(message);
340
- this.raw = raw;
341
- this.name = "NotebookUnavailableError";
342
- }
343
- };
344
- var UnsupportedRuntimeTransportError = class extends Error {
345
- constructor(message) {
346
- super(message);
347
- this.name = "UnsupportedRuntimeTransportError";
348
- }
349
- };
350
-
351
508
  // src/utils/promise.ts
352
509
  var timeout = (prom, time3) => {
353
510
  let timer;
@@ -366,30 +523,35 @@ var once = (fn) => {
366
523
  // src/terminal.ts
367
524
  var TERMINAL_INPUT_FLUSH_DELAY_MS = 8;
368
525
  var terminalInputFlushPattern = /[\x00-\x1f\x7f]/;
369
- var Terminal = class {
526
+ var Terminals = class {
370
527
  constructor(okra) {
371
528
  this.okra = okra;
372
529
  }
373
530
  list() {
374
531
  return this.okra.invoke("terminal.list");
375
532
  }
376
- create(input) {
377
- return this.okra.invoke("terminal.create", input);
378
- }
379
- onStarted(handler) {
380
- return this.okra.listen("terminal.started", handler);
381
- }
382
- resize(id, size) {
383
- return this.okra.invoke("terminal.resize", { id, width: size[0], height: size[1] });
384
- }
385
- onOutput(id, handler) {
386
- return this.okra.listen(`terminal.output.${id}`, handler);
387
- }
388
- input(id, input) {
389
- return Promise.resolve(this.okra.send("terminal.input", { id, input }));
390
- }
391
- listen(event, handler) {
392
- return this.okra.listen(event, handler);
533
+ async create(options = {}) {
534
+ const id = options.id ?? nanoid();
535
+ const handle = this.processHandle(id, options.abortSignal);
536
+ const command = typeof options.command === "string" && options.args?.length ? [options.command, ...options.args] : options.command;
537
+ const { abortSignal, args: _args, ...input } = options;
538
+ try {
539
+ const task = await this.okra.invoke(
540
+ "terminal.create",
541
+ {
542
+ ...input,
543
+ id,
544
+ kind: options.kind ?? "bash",
545
+ size: options.size ?? [80, 24],
546
+ command
547
+ },
548
+ { abortSignal }
549
+ );
550
+ return { ...handle.process, ...task };
551
+ } catch (error51) {
552
+ handle.dispose();
553
+ throw error51;
554
+ }
393
555
  }
394
556
  async attach(id, opts) {
395
557
  const handle = this.processHandle(id, opts?.abortSignal);
@@ -415,31 +577,6 @@ var Terminal = class {
415
577
  throw error51;
416
578
  }
417
579
  }
418
- async spawn(command, args, opts) {
419
- const id = opts?.id || nanoid();
420
- const handle = this.processHandle(id, opts?.abortSignal);
421
- const { abortSignal, ...otherOpts } = opts ?? {};
422
- try {
423
- const result = await this.okra.invoke(
424
- "terminal.spawn",
425
- {
426
- command: [command, ...args],
427
- opts: {
428
- id,
429
- ...otherOpts
430
- }
431
- },
432
- { abortSignal }
433
- );
434
- return {
435
- ...handle.process,
436
- ...result
437
- };
438
- } catch (error51) {
439
- handle.dispose();
440
- throw error51;
441
- }
442
- }
443
580
  processHandle(id, abortSignal) {
444
581
  const disposables = /* @__PURE__ */ new Set();
445
582
  let abortHandler;
@@ -465,7 +602,7 @@ var Terminal = class {
465
602
  }
466
603
  const input2 = pendingInput;
467
604
  pendingInput = "";
468
- void this.input(id, input2);
605
+ void this.okra.send("terminal.input", { id, input: input2 });
469
606
  };
470
607
  const scheduleInputFlush = () => {
471
608
  if (inputFlushTimer) {
@@ -473,7 +610,8 @@ var Terminal = class {
473
610
  }
474
611
  inputFlushTimer = setTimeout(flushInput, TERMINAL_INPUT_FLUSH_DELAY_MS);
475
612
  };
476
- const queueInput = (chunk) => {
613
+ const queueInput = (value) => {
614
+ const chunk = typeof value === "string" ? value : new TextDecoder().decode(value);
477
615
  pendingInput += chunk;
478
616
  if (terminalInputFlushPattern.test(chunk)) {
479
617
  flushInput();
@@ -495,7 +633,7 @@ var Terminal = class {
495
633
  start: (_controller) => {
496
634
  controller = _controller;
497
635
  disposables.add(
498
- this.listen(`terminal.output.${id}`, (data) => {
636
+ this.okra.listen(`terminal.output.${id}`, (data) => {
499
637
  controller?.enqueue(data.output);
500
638
  })
501
639
  );
@@ -507,7 +645,7 @@ var Terminal = class {
507
645
  });
508
646
  const completion = new Promise((resolve) => {
509
647
  disposables.add(
510
- this.listen(`terminal.close.${id}`, (data) => {
648
+ this.okra.listen(`terminal.close.${id}`, (data) => {
511
649
  if (controller) {
512
650
  try {
513
651
  controller.close();
@@ -572,37 +710,9 @@ var Container = class {
572
710
  state() {
573
711
  return this.okra.invoke("container.state");
574
712
  }
575
- openedPorts() {
576
- return this.okra.invoke("container.opened-ports");
577
- }
578
713
  setPhp(version2) {
579
714
  return this.okra.invoke("container.set-php", { version: version2 });
580
715
  }
581
- enableTelemetry(features) {
582
- return this.okra.invoke("container.stream-telemetry", { features: Array.from(features) });
583
- }
584
- stopTelemetry() {
585
- return this.okra.invoke("container.stop-telemetry");
586
- }
587
- listen(event, handler) {
588
- return this.okra.listen(event, handler);
589
- }
590
- onPort(handler) {
591
- let ports = [];
592
- return this.listen("container.ports", (newPorts) => {
593
- for (const port of newPorts) {
594
- if (!ports.some((p) => p.port === port.port)) {
595
- handler(port, "open");
596
- }
597
- }
598
- for (const port of ports) {
599
- if (!newPorts.some((p) => p.port === port.port)) {
600
- handler(port, "close");
601
- }
602
- }
603
- ports = newPorts;
604
- });
605
- }
606
716
  };
607
717
 
608
718
  // src/auth.ts
@@ -689,76 +799,6 @@ var LspConnection = class _LspConnection {
689
799
  }
690
800
  };
691
801
 
692
- // src/composer.ts
693
- var Composer = class {
694
- constructor(okra, client) {
695
- this.okra = okra;
696
- this.credentials = new ComposerCredentials(client, okra.data.id);
697
- }
698
- invoke(command, args = {}, options = {}) {
699
- return this.okra.invoke("composer.invoke", { command, args, options });
700
- }
701
- dumpAutoload() {
702
- return this.okra.invoke("composer.dump-autoload");
703
- }
704
- install(options = {}) {
705
- return this.invoke("install", {}, options);
706
- }
707
- update(args, options = {}) {
708
- return this.invoke("update", args, options);
709
- }
710
- require(packages, options = {}) {
711
- return this.invoke("require", packages, options);
712
- }
713
- remove(args, options = {}) {
714
- return this.invoke("remove", args, options);
715
- }
716
- stream(fn) {
717
- return this.okra.listen("composer.log", fn);
718
- }
719
- packages() {
720
- return this.okra.invoke("composer.packages");
721
- }
722
- };
723
-
724
- // src/log.ts
725
- var Log = class {
726
- constructor(okra) {
727
- this.okra = okra;
728
- }
729
- stream(fn) {
730
- return this.okra.listen("notebook.log", fn);
731
- }
732
- listen(event, handler) {
733
- return this.okra.listen(event, handler);
734
- }
735
- };
736
-
737
- // src/repl.ts
738
- var Repl = class {
739
- constructor(okra) {
740
- this.okra = okra;
741
- }
742
- eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
743
- return this.okra.invoke("repl.eval", { code, args, repl });
744
- }
745
- write(input) {
746
- return this.okra.invoke("repl.write", { input });
747
- }
748
- stop() {
749
- return this.okra.invoke("repl.stop");
750
- }
751
- resize(cols, rows) {
752
- return this.okra.invoke("repl.resize", { cols, rows });
753
- }
754
- listen(event, handler) {
755
- return this.okra.listen(event, handler);
756
- }
757
- onOutput(handler) {
758
- return this.okra.listen("repl.output", handler);
759
- }
760
- };
761
-
762
802
  // src/shell.ts
763
803
  function resolveRunInput(command, argsOrOpts = [], opts) {
764
804
  const args = Array.isArray(argsOrOpts) ? argsOrOpts : [];
@@ -788,7 +828,7 @@ var ProcessResult = class {
788
828
  return this;
789
829
  }
790
830
  };
791
- var OutputStream = class {
831
+ var ProcessReadable = class {
792
832
  constructor() {
793
833
  this.controller = null;
794
834
  this.closed = false;
@@ -828,9 +868,9 @@ var ShellProcess = class {
828
868
  constructor(id, okra, command, opts) {
829
869
  this.id = id;
830
870
  this.okra = okra;
831
- const output = new OutputStream();
832
- const stdout = new OutputStream();
833
- const stderr = new OutputStream();
871
+ const output = new ProcessReadable();
872
+ const stdout = new ProcessReadable();
873
+ const stderr = new ProcessReadable();
834
874
  this.output = output.stream;
835
875
  this.stdout = stdout.stream;
836
876
  this.stderr = stderr.stream;
@@ -899,6 +939,264 @@ var Shell = class {
899
939
  }
900
940
  };
901
941
 
942
+ // src/composer.ts
943
+ var ComposerProcess = class {
944
+ constructor(id, okra, command, args, options) {
945
+ this.id = id;
946
+ this.okra = okra;
947
+ const output = new ProcessReadable();
948
+ const stdout = new ProcessReadable();
949
+ const stderr = new ProcessReadable();
950
+ this.output = output.stream;
951
+ this.stdout = stdout.stream;
952
+ this.stderr = stderr.stream;
953
+ const encoder = new TextEncoder();
954
+ let subscription = this.okra.listen(`composer.output.${this.id}`, (event) => {
955
+ const data = encoder.encode(event.output);
956
+ const source = event.source ?? "output";
957
+ output.enqueue({ source, data });
958
+ if (source === "stderr") {
959
+ stderr.enqueue(data);
960
+ } else {
961
+ stdout.enqueue(data);
962
+ }
963
+ });
964
+ const finish = () => {
965
+ subscription?.dispose();
966
+ subscription = null;
967
+ output.close();
968
+ stdout.close();
969
+ stderr.close();
970
+ };
971
+ const fail = (error51) => {
972
+ subscription?.dispose();
973
+ subscription = null;
974
+ output.fail(error51);
975
+ stdout.fail(error51);
976
+ stderr.fail(error51);
977
+ };
978
+ this.completion = this.okra.invoke("composer.invoke", { id, command, args, options }).then((result) => {
979
+ finish();
980
+ return new ProcessResult(result.output, result.stdout, result.stderr, result.exitCode);
981
+ }).catch((error51) => {
982
+ fail(error51);
983
+ throw error51;
984
+ });
985
+ }
986
+ wait() {
987
+ return this.completion;
988
+ }
989
+ async kill() {
990
+ await this.okra.invoke("composer.kill", { id: this.id });
991
+ }
992
+ };
993
+ var Composer = class {
994
+ constructor(okra, client) {
995
+ this.okra = okra;
996
+ this.credentials = new ComposerCredentials(client, okra.data.id);
997
+ }
998
+ run(command, args = {}, options = {}) {
999
+ return new ComposerProcess(nanoid(), this.okra, command, args, options);
1000
+ }
1001
+ dumpAutoload(options = {}) {
1002
+ return this.run("dump-autoload", {}, options);
1003
+ }
1004
+ install(options = {}) {
1005
+ return this.run("install", {}, options);
1006
+ }
1007
+ update(args, options = {}) {
1008
+ return this.run("update", args, options);
1009
+ }
1010
+ require(packages, options = {}) {
1011
+ return this.run("require", packages, options);
1012
+ }
1013
+ remove(args, options = {}) {
1014
+ return this.run("remove", args, options);
1015
+ }
1016
+ packages() {
1017
+ return this.okra.invoke("composer.packages");
1018
+ }
1019
+ };
1020
+
1021
+ // src/runtime-telemetry.ts
1022
+ var RuntimeTelemetry = class {
1023
+ constructor(okra) {
1024
+ this.okra = okra;
1025
+ this.references = /* @__PURE__ */ new Map();
1026
+ this.synchronization = Promise.resolve();
1027
+ if (okra.runtimeTransport === "realtime") {
1028
+ okra.onDidConnect(() => {
1029
+ if (this.references.size > 0) {
1030
+ void this.synchronize();
1031
+ }
1032
+ });
1033
+ }
1034
+ }
1035
+ async acquire(feature) {
1036
+ this.references.set(feature, (this.references.get(feature) ?? 0) + 1);
1037
+ try {
1038
+ await this.synchronize();
1039
+ } catch (error51) {
1040
+ this.removeReference(feature);
1041
+ throw error51;
1042
+ }
1043
+ let released = false;
1044
+ return async () => {
1045
+ if (released) {
1046
+ return;
1047
+ }
1048
+ released = true;
1049
+ this.removeReference(feature);
1050
+ await this.synchronize();
1051
+ };
1052
+ }
1053
+ removeReference(feature) {
1054
+ const references = this.references.get(feature) ?? 0;
1055
+ if (references <= 1) {
1056
+ this.references.delete(feature);
1057
+ } else {
1058
+ this.references.set(feature, references - 1);
1059
+ }
1060
+ }
1061
+ synchronize() {
1062
+ this.synchronization = this.synchronization.catch(() => {
1063
+ }).then(async () => {
1064
+ const features = Array.from(this.references.keys());
1065
+ if (features.length === 0) {
1066
+ await this.okra.invoke("container.stop-telemetry");
1067
+ } else {
1068
+ await this.okra.invoke("container.stream-telemetry", { features });
1069
+ }
1070
+ });
1071
+ return this.synchronization;
1072
+ }
1073
+ };
1074
+ function watchRuntimeTelemetry(okra, telemetry, feature, event, transform2, options = {}) {
1075
+ let subscription = null;
1076
+ let release = null;
1077
+ let closed = false;
1078
+ let abort = null;
1079
+ const stop = async () => {
1080
+ if (closed) {
1081
+ return;
1082
+ }
1083
+ closed = true;
1084
+ subscription?.dispose();
1085
+ subscription = null;
1086
+ if (abort && options.signal) {
1087
+ options.signal.removeEventListener("abort", abort);
1088
+ }
1089
+ abort = null;
1090
+ await release?.();
1091
+ release = null;
1092
+ };
1093
+ return new ReadableStream({
1094
+ start(controller) {
1095
+ subscription = okra.listen(event, (data) => {
1096
+ if (!closed) {
1097
+ controller.enqueue(transform2(data));
1098
+ }
1099
+ });
1100
+ if (options.signal) {
1101
+ abort = () => {
1102
+ void stop().finally(() => controller.error(options.signal?.reason ?? new Error("Aborted")));
1103
+ };
1104
+ if (options.signal.aborted) {
1105
+ abort();
1106
+ return;
1107
+ }
1108
+ options.signal.addEventListener("abort", abort, { once: true });
1109
+ }
1110
+ void telemetry.acquire(feature).then(async (releaseTelemetry) => {
1111
+ if (closed) {
1112
+ await releaseTelemetry();
1113
+ return;
1114
+ }
1115
+ release = releaseTelemetry;
1116
+ }).catch((error51) => {
1117
+ if (!closed) {
1118
+ closed = true;
1119
+ subscription?.dispose();
1120
+ if (abort && options.signal) {
1121
+ options.signal.removeEventListener("abort", abort);
1122
+ }
1123
+ abort = null;
1124
+ controller.error(error51);
1125
+ }
1126
+ });
1127
+ },
1128
+ cancel: stop
1129
+ });
1130
+ }
1131
+
1132
+ // src/log.ts
1133
+ var Logs = class {
1134
+ constructor(okra, telemetry) {
1135
+ this.okra = okra;
1136
+ this.telemetry = telemetry;
1137
+ }
1138
+ follow(options = {}) {
1139
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "logs", "notebook.log", (message) => ({
1140
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1141
+ source: "runtime",
1142
+ message
1143
+ }), options);
1144
+ }
1145
+ };
1146
+
1147
+ // src/metrics.ts
1148
+ var Metrics = class {
1149
+ constructor(okra, telemetry) {
1150
+ this.okra = okra;
1151
+ this.telemetry = telemetry;
1152
+ }
1153
+ current() {
1154
+ return this.okra.invoke("container.stats");
1155
+ }
1156
+ watch(options = {}) {
1157
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "stats", "container.stats", (stats) => stats, options);
1158
+ }
1159
+ };
1160
+
1161
+ // src/ports.ts
1162
+ var Ports = class {
1163
+ constructor(okra, telemetry) {
1164
+ this.okra = okra;
1165
+ this.telemetry = telemetry;
1166
+ }
1167
+ list() {
1168
+ return this.okra.invoke("container.opened-ports");
1169
+ }
1170
+ watch(options = {}) {
1171
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "ports", "container.ports", (ports) => ports, options);
1172
+ }
1173
+ };
1174
+
1175
+ // src/repl.ts
1176
+ var Repl = class {
1177
+ constructor(okra) {
1178
+ this.okra = okra;
1179
+ }
1180
+ eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
1181
+ return this.okra.invoke("repl.eval", { code, args, repl });
1182
+ }
1183
+ write(input) {
1184
+ return this.okra.invoke("repl.write", { input });
1185
+ }
1186
+ stop() {
1187
+ return this.okra.invoke("repl.stop");
1188
+ }
1189
+ resize(cols, rows) {
1190
+ return this.okra.invoke("repl.resize", { cols, rows });
1191
+ }
1192
+ listen(event, handler) {
1193
+ return this.okra.listen(event, handler);
1194
+ }
1195
+ onOutput(handler) {
1196
+ return this.okra.listen("repl.output", handler);
1197
+ }
1198
+ };
1199
+
902
1200
  // src/services.ts
903
1201
  var notebookBuiltinServices = ["redis"];
904
1202
  var notebookKnownServices = ["start", "nginx", ...notebookBuiltinServices];
@@ -918,12 +1216,6 @@ var Services = class {
918
1216
  stop(name) {
919
1217
  return this.okra.invoke("service.stop", { name });
920
1218
  }
921
- onLog(id, handler) {
922
- return this.okra.listen(`service.log.${id}`, handler);
923
- }
924
- listen(event, handler) {
925
- return this.okra.listen(event, handler);
926
- }
927
1219
  logs(name, options = {}) {
928
1220
  if (options.follow) {
929
1221
  return this.followLogs(name, options);
@@ -976,11 +1268,11 @@ var Services = class {
976
1268
  close();
977
1269
  return this.okra.invoke("service.stop-logs", { id });
978
1270
  });
979
- const output = new ReadableStream({
1271
+ return new ReadableStream({
980
1272
  start: (_controller) => {
981
1273
  controller = _controller;
982
1274
  disposables.add(
983
- this.onLog(id, (data) => {
1275
+ this.okra.listen(`service.log.${id}`, (data) => {
984
1276
  controller?.enqueue(data.output);
985
1277
  })
986
1278
  );
@@ -994,10 +1286,9 @@ var Services = class {
994
1286
  });
995
1287
  },
996
1288
  cancel: () => {
997
- void stop();
1289
+ return stop().then(() => void 0);
998
1290
  }
999
1291
  });
1000
- return { id, output, stop };
1001
1292
  }
1002
1293
  };
1003
1294
 
@@ -19125,6 +19416,7 @@ var restActionRoutes = {
19125
19416
  "container.stop": { method: "POST", path: "/runtime/container/stop" },
19126
19417
  "container.set-php": { method: "PUT", path: "/runtime/container/php" },
19127
19418
  "container.opened-ports": { method: "GET", path: "/runtime/container/ports" },
19419
+ "container.stats": { method: "GET", path: "/runtime/container/stats" },
19128
19420
  "terminal.list": { method: "GET", path: "/runtime/terminals" },
19129
19421
  "shell.exec": { method: "POST", path: "/runtime/executions" },
19130
19422
  "fs.info": { method: "POST", path: "/runtime/files/info" },
@@ -19150,10 +19442,6 @@ var restActionRoutes = {
19150
19442
  "service.logs": { method: "POST", path: "/runtime/services/logs" },
19151
19443
  "composer.invoke": { method: "POST", path: "/runtime/composer/executions" },
19152
19444
  "composer.packages": { method: "GET", path: "/runtime/composer/packages" },
19153
- "composer.dump-autoload": {
19154
- method: "POST",
19155
- path: "/runtime/composer/autoload"
19156
- },
19157
19445
  "git.checkpoint": { method: "POST", path: "/runtime/git/checkpoints" },
19158
19446
  "git.checkout": { method: "POST", path: "/runtime/git/checkouts" },
19159
19447
  "git.log": { method: "POST", path: "/runtime/git/log" },
@@ -19397,7 +19685,7 @@ var NotebookApi = class {
19397
19685
  }
19398
19686
  async create(template, input = {}, init = true) {
19399
19687
  const response = await this.client.post("/notebook", { template, ...input });
19400
- const instance = new NotebookInstance2(response.data, this.client);
19688
+ const instance = new NotebookInstance(response.data, this.client);
19401
19689
  if (!init) {
19402
19690
  return instance;
19403
19691
  }
@@ -19405,25 +19693,25 @@ var NotebookApi = class {
19405
19693
  }
19406
19694
  async get(id) {
19407
19695
  const response = await this.client.get(`/notebook/${id}`);
19408
- return new NotebookInstance2(response.data, this.client);
19696
+ return new NotebookInstance(response.data, this.client);
19409
19697
  }
19410
19698
  async update(id, input = {}) {
19411
19699
  const response = await this.client.put(`/notebook/${id}`, input);
19412
- return new NotebookInstance2(response.data, this.client);
19700
+ return new NotebookInstance(response.data, this.client);
19413
19701
  }
19414
19702
  async destroy(id) {
19415
19703
  await this.client.delete(`/notebook/${id}`);
19416
19704
  }
19417
19705
  async fork(id, input = {}) {
19418
19706
  const response = await this.client.post(`/notebook/${id}/fork`, input);
19419
- return new NotebookInstance2(response.data, this.client);
19707
+ return new NotebookInstance(response.data, this.client);
19420
19708
  }
19421
19709
  async gitCredentials(id) {
19422
19710
  const response = await this.client.post(`/notebook/${id}/git/credentials`);
19423
19711
  return response.data;
19424
19712
  }
19425
19713
  openFromData(data) {
19426
- return this.init(new NotebookInstance2(data, this.client));
19714
+ return this.init(new NotebookInstance(data, this.client));
19427
19715
  }
19428
19716
  async init(instance) {
19429
19717
  await instance.ready();
@@ -19438,7 +19726,7 @@ function normalizeApiBaseUrl(url2) {
19438
19726
  baseUrl.hash = "";
19439
19727
  return baseUrl.toString();
19440
19728
  }
19441
- var Client2 = class {
19729
+ var Client = class {
19442
19730
  constructor(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19443
19731
  this.runtimeTransportValue = "realtime";
19444
19732
  this.fetch = globalThis.fetch;
@@ -19522,7 +19810,7 @@ var Client2 = class {
19522
19810
  return { data: body === "" ? void 0 : JSON.parse(body) };
19523
19811
  }
19524
19812
  };
19525
- var PHPSandbox = class _PHPSandbox extends Client2 {
19813
+ var PHPSandbox = class _PHPSandbox extends Client {
19526
19814
  static realtime(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19527
19815
  return new _PHPSandbox(token, url2, options);
19528
19816
  }
@@ -19630,7 +19918,7 @@ var NotebookMailApi = class {
19630
19918
  }
19631
19919
  };
19632
19920
  var _initPromise, _NotebookInstance_instances, init_fn;
19633
- var NotebookInstance2 = class {
19921
+ var NotebookInstance = class {
19634
19922
  constructor(data, client) {
19635
19923
  this.data = data;
19636
19924
  this.client = client;
@@ -19665,15 +19953,18 @@ var NotebookInstance2 = class {
19665
19953
  this.watchConnection();
19666
19954
  __privateSet(this, _initPromise, __privateMethod(this, _NotebookInstance_instances, init_fn).call(this));
19667
19955
  }
19668
- this.file = new Filesystem(
19956
+ this.files = new Filesystem(
19669
19957
  this,
19670
19958
  this.socket === null ? null : (handler) => this.onDidConnect(handler)
19671
19959
  );
19672
- this.terminal = new Terminal(this);
19960
+ this.terminals = new Terminals(this);
19673
19961
  this.auth = new Auth(this);
19674
19962
  this.lsp = new Lsp(this);
19675
19963
  this.composer = new Composer(this, client);
19676
- this.log = new Log(this);
19964
+ const telemetry = new RuntimeTelemetry(this);
19965
+ this.logs = new Logs(this, telemetry);
19966
+ this.metrics = new Metrics(this, telemetry);
19967
+ this.ports = new Ports(this, telemetry);
19677
19968
  this.repl = new Repl(this);
19678
19969
  this.container = new Container(this);
19679
19970
  this.shell = new Shell(this);
@@ -19719,6 +20010,12 @@ var NotebookInstance2 = class {
19719
20010
  }
19720
20011
  return this.shell.run(command, argsOrOpts);
19721
20012
  }
20013
+ exec(command, argsOrOpts = [], opts) {
20014
+ if (Array.isArray(argsOrOpts)) {
20015
+ return this.run(command, argsOrOpts, opts).wait();
20016
+ }
20017
+ return this.run(command, argsOrOpts).wait();
20018
+ }
19722
20019
  gitCredentials() {
19723
20020
  return this.client.notebook.gitCredentials(this.data.id);
19724
20021
  }
@@ -19809,9 +20106,6 @@ var NotebookInstance2 = class {
19809
20106
  const socket = this.realtimeSocket();
19810
20107
  socket.onDidConnect(() => {
19811
20108
  socket.emit("okra.connected");
19812
- if (this.client.options.telemetry) {
19813
- this.container.enableTelemetry(this.client.options.telemetry);
19814
- }
19815
20109
  });
19816
20110
  socket.onDidClose(() => {
19817
20111
  socket.emit("okra.disconnected");
@@ -19988,7 +20282,7 @@ function formatQueryString(params) {
19988
20282
  }
19989
20283
  export {
19990
20284
  ApiError,
19991
- Client2 as Client,
20285
+ Client,
19992
20286
  CommandError,
19993
20287
  ComposerCredentials,
19994
20288
  ErrorEvent,
@@ -20002,7 +20296,7 @@ export {
20002
20296
  NotebookApi,
20003
20297
  NotebookComposerCredentialApi,
20004
20298
  NotebookInitError,
20005
- NotebookInstance2 as NotebookInstance,
20299
+ NotebookInstance,
20006
20300
  NotebookMail,
20007
20301
  NotebookMailApi,
20008
20302
  NotebookPreview,
@@ -20014,6 +20308,7 @@ export {
20014
20308
  NotebookUnavailableError,
20015
20309
  PHPSandbox,
20016
20310
  PHPSandboxError,
20311
+ ProcessReadable,
20017
20312
  ProcessResult,
20018
20313
  PromiseTimeoutError,
20019
20314
  PublicationApi,
@@ -20048,6 +20343,7 @@ export {
20048
20343
  PublishStreamSsePhaseEventSchema,
20049
20344
  PublishStreamSseResultEventSchema,
20050
20345
  RateLimitError,
20346
+ RuntimeTelemetry,
20051
20347
  SendTimeoutError,
20052
20348
  ServerApi,
20053
20349
  ServerInstance,
@@ -20056,7 +20352,8 @@ export {
20056
20352
  notebookBuiltinServices,
20057
20353
  notebookKnownServices,
20058
20354
  once,
20059
- timeout
20355
+ timeout,
20356
+ watchRuntimeTelemetry
20060
20357
  };
20061
20358
  /*! Bundled license information:
20062
20359