@slicervm/sdk 0.1.5 → 0.1.6

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
@@ -976,8 +976,8 @@ var VMBg = class {
976
976
  q.set("cmd", req.command);
977
977
  for (const a of req.args ?? []) q.append("args", a);
978
978
  for (const e of req.env ?? []) q.append("env", e);
979
- if (req.uid !== void 0 && req.uid !== 0) q.set("uid", String(req.uid));
980
- if (req.gid !== void 0 && req.gid !== 0) q.set("gid", String(req.gid));
979
+ if (req.uid !== void 0) q.set("uid", String(req.uid));
980
+ if (req.gid !== void 0) q.set("gid", String(req.gid));
981
981
  if (req.shell) q.set("shell", req.shell);
982
982
  if (req.cwd) q.set("cwd", req.cwd);
983
983
  if (req.ringBytes !== void 0 && req.ringBytes > 0) {
@@ -1289,8 +1289,153 @@ var SlicerClient = class _SlicerClient {
1289
1289
  }
1290
1290
  };
1291
1291
 
1292
+ // src/shell.ts
1293
+ var FRAME_TYPE_DATA = 1;
1294
+ var FRAME_TYPE_WINDOW_SIZE = 2;
1295
+ var FRAME_TYPE_SHUTDOWN = 3;
1296
+ var FRAME_TYPE_HEARTBEAT = 4;
1297
+ var FRAME_TYPE_SESSION_CLOSE = 5;
1298
+ var HEADER_SIZE = 5;
1299
+ function encodeFrame(frameType, payload) {
1300
+ const payloadLen = payload ? payload.byteLength : 0;
1301
+ const buf = new Uint8Array(HEADER_SIZE + payloadLen);
1302
+ const view = new DataView(buf.buffer);
1303
+ view.setUint8(0, frameType);
1304
+ view.setUint32(1, payloadLen, false);
1305
+ if (payload) buf.set(payload, HEADER_SIZE);
1306
+ return buf;
1307
+ }
1308
+ function parseFrame(data) {
1309
+ if (data.byteLength < HEADER_SIZE) return null;
1310
+ const view = new DataView(data);
1311
+ const frameType = view.getUint8(0);
1312
+ const payloadLen = view.getUint32(1, false);
1313
+ if (data.byteLength < HEADER_SIZE + payloadLen) return null;
1314
+ const payload = new Uint8Array(data, HEADER_SIZE, payloadLen);
1315
+ return { frameType, payload };
1316
+ }
1317
+ var encoder = new TextEncoder();
1318
+ var decoder = new TextDecoder();
1319
+ var SlicerShellSession = class {
1320
+ constructor(terminal, options) {
1321
+ this.terminal = terminal;
1322
+ this.options = options;
1323
+ }
1324
+ terminal;
1325
+ options;
1326
+ ws = null;
1327
+ heartbeatTimer = null;
1328
+ dataDisposable = null;
1329
+ /** True when the WebSocket is open and relaying. */
1330
+ get connected() {
1331
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
1332
+ }
1333
+ /** Open the WebSocket and begin relaying. */
1334
+ connect() {
1335
+ if (this.ws) return;
1336
+ this.options.onStateChange?.("connecting");
1337
+ const ws = new WebSocket(this.options.url);
1338
+ ws.binaryType = "arraybuffer";
1339
+ this.ws = ws;
1340
+ ws.onopen = () => {
1341
+ this.options.onStateChange?.("connected");
1342
+ this.terminal.reset();
1343
+ this.sendResize(this.terminal.cols, this.terminal.rows);
1344
+ this.startHeartbeat();
1345
+ };
1346
+ ws.onmessage = (ev) => {
1347
+ if (!(ev.data instanceof ArrayBuffer)) return;
1348
+ const frame = parseFrame(ev.data);
1349
+ if (!frame) return;
1350
+ switch (frame.frameType) {
1351
+ case FRAME_TYPE_DATA:
1352
+ this.terminal.write(decoder.decode(frame.payload));
1353
+ break;
1354
+ case FRAME_TYPE_SHUTDOWN:
1355
+ case FRAME_TYPE_SESSION_CLOSE:
1356
+ this.teardown();
1357
+ break;
1358
+ }
1359
+ };
1360
+ ws.onclose = () => {
1361
+ this.teardown();
1362
+ };
1363
+ ws.onerror = () => {
1364
+ this.options.onError?.("WebSocket error");
1365
+ this.teardown();
1366
+ };
1367
+ this.dataDisposable = this.terminal.onData((data) => {
1368
+ if (!this.connected) return;
1369
+ const payload = encoder.encode(data);
1370
+ this.ws.send(encodeFrame(FRAME_TYPE_DATA, payload));
1371
+ });
1372
+ }
1373
+ /** Send a graceful shutdown frame and close. */
1374
+ disconnect() {
1375
+ if (this.ws) {
1376
+ try {
1377
+ this.ws.send(encodeFrame(FRAME_TYPE_SHUTDOWN));
1378
+ } catch {
1379
+ }
1380
+ }
1381
+ this.teardown();
1382
+ }
1383
+ /** Send a window resize. Call this from FitAddon's onResize or a ResizeObserver. */
1384
+ resize(cols, rows) {
1385
+ if (!this.connected) return;
1386
+ this.sendResize(cols, rows);
1387
+ }
1388
+ // --- internals -------------------------------------------------------------
1389
+ sendResize(cols, rows) {
1390
+ const payload = new Uint8Array(8);
1391
+ const view = new DataView(payload.buffer);
1392
+ view.setUint32(0, cols, false);
1393
+ view.setUint32(4, rows, false);
1394
+ this.ws.send(encodeFrame(FRAME_TYPE_WINDOW_SIZE, payload));
1395
+ }
1396
+ startHeartbeat() {
1397
+ this.stopHeartbeat();
1398
+ const intervalMs = this.options.heartbeatIntervalMs ?? 3e4;
1399
+ this.heartbeatTimer = setInterval(() => {
1400
+ if (!this.connected) return;
1401
+ this.ws.send(encodeFrame(FRAME_TYPE_HEARTBEAT));
1402
+ }, intervalMs);
1403
+ }
1404
+ stopHeartbeat() {
1405
+ if (this.heartbeatTimer !== null) {
1406
+ clearInterval(this.heartbeatTimer);
1407
+ this.heartbeatTimer = null;
1408
+ }
1409
+ }
1410
+ teardown() {
1411
+ this.stopHeartbeat();
1412
+ if (this.dataDisposable) {
1413
+ this.dataDisposable.dispose();
1414
+ this.dataDisposable = null;
1415
+ }
1416
+ if (this.ws) {
1417
+ const ws = this.ws;
1418
+ this.ws = null;
1419
+ ws.onopen = null;
1420
+ ws.onmessage = null;
1421
+ ws.onclose = null;
1422
+ ws.onerror = null;
1423
+ try {
1424
+ ws.close();
1425
+ } catch {
1426
+ }
1427
+ }
1428
+ this.options.onStateChange?.("disconnected");
1429
+ }
1430
+ };
1431
+
1292
1432
  exports.ExecStdioBase64 = ExecStdioBase64;
1293
1433
  exports.ExecStdioText = ExecStdioText;
1434
+ exports.FRAME_TYPE_DATA = FRAME_TYPE_DATA;
1435
+ exports.FRAME_TYPE_HEARTBEAT = FRAME_TYPE_HEARTBEAT;
1436
+ exports.FRAME_TYPE_SESSION_CLOSE = FRAME_TYPE_SESSION_CLOSE;
1437
+ exports.FRAME_TYPE_SHUTDOWN = FRAME_TYPE_SHUTDOWN;
1438
+ exports.FRAME_TYPE_WINDOW_SIZE = FRAME_TYPE_WINDOW_SIZE;
1294
1439
  exports.Forwarder = Forwarder;
1295
1440
  exports.GiB = GiB;
1296
1441
  exports.HostGroupsAPI = HostGroupsAPI;
@@ -1300,11 +1445,14 @@ exports.SecretExistsError = SecretExistsError;
1300
1445
  exports.SecretsAPI = SecretsAPI;
1301
1446
  exports.SlicerAPIError = SlicerAPIError;
1302
1447
  exports.SlicerClient = SlicerClient;
1448
+ exports.SlicerShellSession = SlicerShellSession;
1303
1449
  exports.VM = VM;
1304
1450
  exports.VMBg = VMBg;
1305
1451
  exports.VMFileSystem = VMFileSystem;
1306
1452
  exports.VMsAPI = VMsAPI;
1453
+ exports.encodeFrame = encodeFrame;
1307
1454
  exports.parseAddressMapping = parseAddressMapping;
1455
+ exports.parseFrame = parseFrame;
1308
1456
  exports.resolveTransport = resolveTransport;
1309
1457
  //# sourceMappingURL=index.cjs.map
1310
1458
  //# sourceMappingURL=index.cjs.map