@phpsandbox/sdk 0.0.30 → 0.0.33

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/README.md CHANGED
@@ -79,12 +79,20 @@ await fetched.ready();
79
79
 
80
80
  const forked = await created.fork();
81
81
  await forked.delete();
82
+
83
+ const persistent = await client.notebook.create('laravel', {
84
+ title: 'Persistent Laravel',
85
+ persistent: true,
86
+ });
87
+
88
+ console.log(persistent.data.policy);
82
89
  ```
83
90
 
84
91
  Notes:
85
92
 
86
93
  - `create()` and `fork()` initialize automatically.
87
94
  - `open()` and `get()` return a notebook instance; call `await notebook.ready()` before using tools.
95
+ - API notebook creates are ephemeral by default. `persistent: true` requires an entitled account.
88
96
 
89
97
  ## Services At A Glance
90
98
 
@@ -103,6 +111,12 @@ Each `NotebookInstance` exposes service clients:
103
111
  - `notebook.log` (`Log`)
104
112
  - `notebook.services` (`Services`)
105
113
 
114
+ Notes:
115
+
116
+ - `terminal.spawn()` and `shell.exec()` are part of the current okra websocket action set. `terminal.start` currently is not.
117
+ - `notebook.repl.eval()` is part of the current okra websocket action set. `repl.start` is currently not.
118
+ - `notebook.laravel` is exposed by the SDK, but the current okra websocket action set does not expose Laravel maintenance actions.
119
+
106
120
  ## Common Operations
107
121
 
108
122
  ### Files
@@ -148,9 +162,14 @@ console.log(result.output);
148
162
  ### Services
149
163
 
150
164
  ```ts
165
+ import { notebookBuiltinServices } from '@phpsandbox/sdk';
166
+
151
167
  const services = await notebook.services.list();
152
168
 
169
+ console.log(notebookBuiltinServices); // ['redis']
170
+
153
171
  await notebook.services.run('redis');
172
+ await notebook.services.run('queue', 'php artisan queue:work');
154
173
 
155
174
  const snapshot = await notebook.services.logs('redis', { tail: 50 });
156
175
  console.log(snapshot);
@@ -639,19 +639,30 @@ var Terminal = class {
639
639
  },
640
640
  close: dispose
641
641
  });
642
+ let controller = null;
642
643
  const output = new ReadableStream({
643
- start: (controller) => {
644
+ start: (_controller) => {
645
+ controller = _controller;
644
646
  disposables.add(
645
647
  this.listen(`terminal.output.${id}`, (data) => {
646
- controller.enqueue(data.output);
648
+ controller?.enqueue(data.output);
647
649
  })
648
650
  );
649
651
  },
650
- cancel: dispose
652
+ cancel: () => {
653
+ controller = null;
654
+ dispose();
655
+ }
651
656
  });
652
657
  const exit = new Promise((resolve) => {
653
658
  disposables.add(
654
659
  this.listen(`terminal.close.${id}`, (data) => {
660
+ if (controller) {
661
+ try {
662
+ controller.close();
663
+ } catch {
664
+ }
665
+ }
655
666
  dispose();
656
667
  resolve(data.exitCode);
657
668
  })
@@ -1027,6 +1038,8 @@ var Shell = class {
1027
1038
  };
1028
1039
 
1029
1040
  // src/services.ts
1041
+ var notebookBuiltinServices = ["redis"];
1042
+ var notebookKnownServices = ["start", "nginx", ...notebookBuiltinServices];
1030
1043
  var Services = class {
1031
1044
  constructor(okra) {
1032
1045
  this.okra = okra;
@@ -1034,8 +1047,11 @@ var Services = class {
1034
1047
  list() {
1035
1048
  return this.okra.invoke("service.list");
1036
1049
  }
1037
- run(name) {
1038
- return this.okra.invoke("service.run", { name });
1050
+ run(name, command) {
1051
+ return this.okra.invoke("service.run", {
1052
+ name,
1053
+ command
1054
+ });
1039
1055
  }
1040
1056
  stop(name) {
1041
1057
  return this.okra.invoke("service.stop", { name });
@@ -3177,7 +3193,7 @@ var Transport = class {
3177
3193
  this.connectionStats.connectionStartTime = Date.now();
3178
3194
  const startClosed = options.startClosed !== false;
3179
3195
  this.rws = new reconnecting_websocket_mjs_default(this.url.toString(), [], {
3180
- WebSocket: globalThis.WebSocket ?? browser_default,
3196
+ WebSocket: options.webSocket ?? globalThis.WebSocket ?? browser_default,
3181
3197
  connectionTimeout: options.connectionTimeout,
3182
3198
  maxReconnectionDelay: 2e3,
3183
3199
  minReconnectionDelay: 200,
@@ -3232,7 +3248,19 @@ var Transport = class {
3232
3248
  async registerWatchers() {
3233
3249
  const onMessage = (ev) => {
3234
3250
  if (!(ev.data instanceof Blob)) {
3235
- throw new Error("Unexpected message type: " + typeof ev.data);
3251
+ const error = new Error("Unexpected message type: " + typeof ev.data);
3252
+ this.connectionStats.totalErrors++;
3253
+ this.log("error", "Unexpected WebSocket message type", {
3254
+ error: error.message,
3255
+ messageType: typeof ev.data
3256
+ });
3257
+ this.eventEmitter.emit("transport.error", {
3258
+ type: "message_type_error",
3259
+ error,
3260
+ rawMessage: ev.data,
3261
+ timestamp: Date.now()
3262
+ });
3263
+ return;
3236
3264
  }
3237
3265
  ev.data.arrayBuffer().then((buffer) => {
3238
3266
  if (buffer.byteLength === 0) {
@@ -3240,7 +3268,19 @@ var Transport = class {
3240
3268
  return;
3241
3269
  }
3242
3270
  try {
3243
- this.handleRawMessage(decode(buffer));
3271
+ void this.handleRawMessage(decode(buffer)).catch((error) => {
3272
+ this.connectionStats.totalErrors++;
3273
+ this.log("error", "Failed to process WebSocket frame", {
3274
+ error: error instanceof Error ? error.message : String(error),
3275
+ byteLength: buffer.byteLength
3276
+ });
3277
+ this.eventEmitter.emit("transport.error", {
3278
+ type: "message_handle_error",
3279
+ error,
3280
+ rawMessage: buffer,
3281
+ timestamp: Date.now()
3282
+ });
3283
+ });
3244
3284
  } catch (error) {
3245
3285
  this.connectionStats.totalErrors++;
3246
3286
  this.log("error", "Failed to decode WebSocket frame", {
@@ -3264,6 +3304,10 @@ var Transport = class {
3264
3304
  });
3265
3305
  }
3266
3306
  async handleRawMessage(ev) {
3307
+ if (this.closed) {
3308
+ this.log("debug", "Ignoring message received after transport was closed");
3309
+ return;
3310
+ }
3267
3311
  if (typeof ev !== "object" || ev === null) {
3268
3312
  this.log("debug", "Received invalid message format", { ev });
3269
3313
  return;
@@ -4925,7 +4969,8 @@ var NotebookInstance = class {
4925
4969
  this.emitter = EventManager.createInstance();
4926
4970
  this.socket = new Transport(data.okraUrl, this.emitter, {
4927
4971
  debug: client.options.debug,
4928
- startClosed: client.options.startClosed
4972
+ startClosed: client.options.startClosed,
4973
+ webSocket: client.options.webSocket
4929
4974
  });
4930
4975
  this.watchConnection();
4931
4976
  __privateSet(this, _initPromise, __privateMethod(this, _NotebookInstance_instances, init_fn).call(this));
@@ -5093,6 +5138,8 @@ init_fn = function() {
5093
5138
  reject(error);
5094
5139
  });
5095
5140
  }));
5141
+ void __privateGet(this, _initPromise).catch(() => {
5142
+ });
5096
5143
  return __privateGet(this, _initPromise);
5097
5144
  };
5098
5145
  var NotebookSecrets = class {
@@ -5144,6 +5191,8 @@ export {
5144
5191
  Transport,
5145
5192
  createBeacon,
5146
5193
  isBeaconSupported,
5194
+ notebookBuiltinServices,
5195
+ notebookKnownServices,
5147
5196
  once,
5148
5197
  timeout
5149
5198
  };