@opengeni/sdk 0.3.1 → 0.5.0

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.js CHANGED
@@ -224,6 +224,9 @@ var OpenGeniClient = class {
224
224
  async getSession(workspaceId, sessionId) {
225
225
  return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`);
226
226
  }
227
+ async updateSession(workspaceId, sessionId, request) {
228
+ return await this.requestJson("PATCH", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`, request);
229
+ }
227
230
  async listSessions(workspaceId, options = {}) {
228
231
  return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/sessions`, void 0, {
229
232
  ...options.limit !== void 0 ? { limit: String(options.limit) } : {}
@@ -426,6 +429,127 @@ var OpenGeniClient = class {
426
429
  async compactSessionContext(workspaceId, sessionId) {
427
430
  return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/compact`, {});
428
431
  }
432
+ // --- Channel-A structured services (P4.4) ------------------------------------
433
+ // FileSystem (Pierre tree), Git (Pierre diff), Terminal (exec + PTY). Each is a
434
+ // synchronous API-direct point query; the fs.changed/git.changed/terminal.pty.*
435
+ // notifications + the PTY output stream arrive on the existing event SSE.
436
+ /** FileSystem: list a directory tree (feeds the Pierre file tree). */
437
+ async fsList(workspaceId, sessionId, request = {}) {
438
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`, request);
439
+ }
440
+ /** FileSystem: read a file (text or base64; binary-safe, size-capped). */
441
+ async fsRead(workspaceId, sessionId, request) {
442
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`, request);
443
+ }
444
+ /** FileSystem: write a file (last-writer-wins; emits fs.changed). */
445
+ async fsWrite(workspaceId, sessionId, request) {
446
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/write`, request);
447
+ }
448
+ /** FileSystem: delete a path (emits fs.changed). */
449
+ async fsDelete(workspaceId, sessionId, request) {
450
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/delete`, request);
451
+ }
452
+ /** FileSystem: move/rename a path (emits fs.changed; 409 if destination exists and overwrite is false). */
453
+ async fsMove(workspaceId, sessionId, request) {
454
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/move`, request);
455
+ }
456
+ /** FileSystem: create a directory (emits fs.changed; recursive defaults to true). */
457
+ async fsMkdir(workspaceId, sessionId, request) {
458
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/mkdir`, request);
459
+ }
460
+ /** Git: working-tree/index status (the Pierre file-status feed). */
461
+ async gitStatus(workspaceId, sessionId, request = {}) {
462
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`, request);
463
+ }
464
+ /** Git: structured diff hunks (the Pierre diff feed). */
465
+ async gitDiff(workspaceId, sessionId, request = {}) {
466
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`, request);
467
+ }
468
+ /** Git: commit log. */
469
+ async gitLog(workspaceId, sessionId, request = {}) {
470
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/log`, request);
471
+ }
472
+ /** Git: show a commit (diff vs first parent) or fetch a raw blob at a ref. */
473
+ async gitShow(workspaceId, sessionId, request) {
474
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/show`, request);
475
+ }
476
+ /** Terminal: run a bounded command, returning buffered stdout/stderr inline. */
477
+ async terminalExec(workspaceId, sessionId, request) {
478
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/exec`, request);
479
+ }
480
+ /** Terminal: open an interactive PTY. Output streams on the event SSE as
481
+ * terminal.pty.output.delta; drive it with terminalPtyWrite. */
482
+ async terminalPtyOpen(workspaceId, sessionId, request = {}) {
483
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty`, request);
484
+ }
485
+ /** Terminal: send stdin to an open PTY (output rides A1). */
486
+ async terminalPtyWrite(workspaceId, sessionId, request) {
487
+ await this.requestVoid("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/write`, request);
488
+ }
489
+ /** Terminal: resize an open PTY. */
490
+ async terminalPtyResize(workspaceId, sessionId, request) {
491
+ await this.requestVoid("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/resize`, request);
492
+ }
493
+ /** Terminal: close an open PTY (idempotent). */
494
+ async terminalPtyClose(workspaceId, sessionId, request) {
495
+ await this.requestVoid("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/close`, request);
496
+ }
497
+ // --- Stream surfacing: capability negotiation + viewer lifecycle (Phase 5) ---
498
+ // The capability doc is the single source of UI truth (degradation is always a
499
+ // value, never a crash). The desktop pixel plane (Channel B) is gated behind an
500
+ // un-redacted-acknowledgment + a viewer holder; the structured terminal/files/
501
+ // git surfaces (Channel A) ride the methods above and the event SSE.
502
+ /** Read the negotiated capability doc for a session WITHOUT acquiring a viewer
503
+ * holder (no warm, no spawn). Drives capability-gated rendering: which
504
+ * surfaces mount, the per-surface unavailability reasons, and the lease
505
+ * liveness the client polls on while `cold`/`warming`. The desktop URL/token
506
+ * are minted in-process only when the box is warm AND the principal has
507
+ * acknowledged the un-redacted plane. */
508
+ async getStreamCapabilities(workspaceId, sessionId) {
509
+ return await this.requestJson(
510
+ "GET",
511
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`
512
+ );
513
+ }
514
+ /** Record the calling principal's acknowledgment of the un-redacted desktop
515
+ * pixel plane (and, when the box is shared, the shared-exposure disclosure).
516
+ * The desktop viewer-attach path returns 409 until this is recorded. */
517
+ async acknowledgeStream(workspaceId, sessionId, request = {}) {
518
+ return await this.requestJson(
519
+ "POST",
520
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities/acknowledge`,
521
+ request
522
+ );
523
+ }
524
+ /** Attach a viewer holder (refcounted liveness — keeps the box warm while
525
+ * watched/used), spinning the box up in-process when cold, and mint the scoped
526
+ * direct-to-provider URLs for the requested plane(s). `request.desktop:true`
527
+ * opts into the un-redacted pixel plane and mints the noVNC URL — that plane
528
+ * alone throws `OpenGeniApiError(409)` when the un-redacted/shared
529
+ * acknowledgment is missing (the consent gate). A terminal-only attach
530
+ * (`desktop` omitted/false) warms the box + mints the pty-ws terminal cell with
531
+ * NO consent gate. An omitted `viewerId` mints a fresh one. */
532
+ async attachViewer(workspaceId, sessionId, request = {}) {
533
+ return await this.requestJson(
534
+ "POST",
535
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers`,
536
+ request
537
+ );
538
+ }
539
+ /** Heartbeat a viewer holder (Channel-A app-level liveness). A closed laptop
540
+ * stops sending these → the reaper drops the holder within ~90s. Echoes
541
+ * `leaseEpoch` so a superseded epoch is rejected (`alive:false` → re-attach). */
542
+ async heartbeatViewer(workspaceId, sessionId, viewerId, request) {
543
+ return await this.requestJson(
544
+ "POST",
545
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}/heartbeat`,
546
+ request
547
+ );
548
+ }
549
+ /** Detach a viewer (delete this holder; idempotent delete-my-row). */
550
+ async detachViewer(workspaceId, sessionId, viewerId) {
551
+ await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}`);
552
+ }
429
553
  // --- Access + workspaces -----------------------------------------------------
430
554
  /**
431
555
  * The deployment's public client bootstrap config: the host-exposed models
@@ -632,6 +756,16 @@ var OpenGeniClient = class {
632
756
  `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}/reindex`
633
757
  );
634
758
  }
759
+ /**
760
+ * Delete a document from a base. Removes the document row and its indexed
761
+ * chunks while leaving the uploaded file asset available for other uses.
762
+ */
763
+ async deleteDocument(workspaceId, baseId, documentId) {
764
+ await this.requestVoid(
765
+ "DELETE",
766
+ `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}`
767
+ );
768
+ }
635
769
  async searchDocuments(workspaceId, baseId, request) {
636
770
  return await this.requestJson(
637
771
  "POST",
@@ -909,6 +1043,94 @@ function proxySessionEventStream(client, workspaceId, sessionId, options = {}) {
909
1043
  });
910
1044
  }
911
1045
 
1046
+ // src/desktop.ts
1047
+ function desktopSocketUrl(cap) {
1048
+ if (!cap.url) {
1049
+ throw new Error("desktop capability has no url (transport is null)");
1050
+ }
1051
+ const u = new URL(cap.url);
1052
+ if (u.protocol === "https:") {
1053
+ u.protocol = "wss:";
1054
+ } else if (u.protocol === "http:") {
1055
+ u.protocol = "ws:";
1056
+ }
1057
+ if (/\/vnc(_lite)?\.html$/.test(u.pathname)) {
1058
+ u.pathname = u.pathname.replace(/\/vnc(_lite)?\.html$/, "/websockify");
1059
+ u.search = "";
1060
+ u.hash = "";
1061
+ }
1062
+ return u.toString();
1063
+ }
1064
+ function nextDesktopState(current, ev) {
1065
+ switch (ev.type) {
1066
+ case "negotiated":
1067
+ return "connecting";
1068
+ case "connected":
1069
+ return "connected";
1070
+ case "rotate":
1071
+ return current === "connected" ? "rotating" : current;
1072
+ case "disconnected":
1073
+ return current === "ended" ? "ended" : "reconnecting";
1074
+ case "fail":
1075
+ return "error";
1076
+ case "abort":
1077
+ return "ended";
1078
+ default:
1079
+ return current;
1080
+ }
1081
+ }
1082
+ function applyUrlRotation(cap, payload, knownEpoch) {
1083
+ if (payload.leaseEpoch < knownEpoch) {
1084
+ return null;
1085
+ }
1086
+ return { ...cap, url: payload.url, token: payload.token, expiresAt: payload.expiresAt };
1087
+ }
1088
+
1089
+ // src/terminal.ts
1090
+ function terminalSocketUrl(cap) {
1091
+ if (!cap.url) {
1092
+ throw new Error("terminal capability has no url (transport is not pty-ws)");
1093
+ }
1094
+ const u = new URL(cap.url);
1095
+ if (u.protocol === "https:") {
1096
+ u.protocol = "wss:";
1097
+ } else if (u.protocol === "http:") {
1098
+ u.protocol = "ws:";
1099
+ }
1100
+ return u.toString();
1101
+ }
1102
+ var TTYD_SUBPROTOCOL = "tty";
1103
+ var TtydClientCommand = {
1104
+ /** stdin: "0" + raw input bytes. */
1105
+ INPUT: "0",
1106
+ /** window resize: "1" + JSON.stringify({ columns, rows }). */
1107
+ RESIZE: "1",
1108
+ /** flow-control pause (back-pressure): "2". */
1109
+ PAUSE: "2",
1110
+ /** flow-control resume: "3". */
1111
+ RESUME: "3"
1112
+ };
1113
+ var TtydServerCommand = {
1114
+ /** stdout/stderr: "0" + raw output bytes (write the rest into xterm). */
1115
+ OUTPUT: "0",
1116
+ /** set the window title: "1" + title string. */
1117
+ SET_WINDOW_TITLE: "1",
1118
+ /** ttyd client preferences JSON: "2" + json (ignored by us). */
1119
+ SET_PREFERENCES: "2"
1120
+ };
1121
+ function ttydAuthFrame(opts) {
1122
+ const frame = { AuthToken: "" };
1123
+ if (opts?.columns && opts.columns > 0) frame.columns = opts.columns;
1124
+ if (opts?.rows && opts.rows > 0) frame.rows = opts.rows;
1125
+ return JSON.stringify(frame);
1126
+ }
1127
+ function ttydInputFrame(data) {
1128
+ return TtydClientCommand.INPUT + data;
1129
+ }
1130
+ function ttydResizeFrame(columns, rows) {
1131
+ return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });
1132
+ }
1133
+
912
1134
  // src/types.ts
913
1135
  var SESSION_EVENT_TYPES = [
914
1136
  "session.created",
@@ -942,7 +1164,25 @@ var SESSION_EVENT_TYPES = [
942
1164
  "goal.completed",
943
1165
  "goal.paused",
944
1166
  "goal.resumed",
945
- "goal.continuation"
1167
+ "goal.continuation",
1168
+ // Channel-B desktop pixel-plane signals (mirror of contracts SessionEventType;
1169
+ // the contract-parity test asserts sorted equality).
1170
+ "stream.url.rotated",
1171
+ "stream.opened",
1172
+ "stream.closed",
1173
+ "stream.revoked",
1174
+ // Channel-B recording signals (P4.3 — "agent films itself proving the fix").
1175
+ "recording.started",
1176
+ "recording.available",
1177
+ "recording.failed",
1178
+ // Channel-A structured-service notifications (P4.4; mirror of contracts
1179
+ // SessionEventType — the contract-parity test asserts sorted equality).
1180
+ "fs.changed",
1181
+ "git.changed",
1182
+ "terminal.pty.started",
1183
+ "terminal.pty.output.delta",
1184
+ "terminal.pty.exited",
1185
+ "session.title_set"
946
1186
  ];
947
1187
  var KNOWN_PERMISSIONS = [
948
1188
  "account:read",
@@ -956,8 +1196,17 @@ var KNOWN_PERMISSIONS = [
956
1196
  "sessions:create",
957
1197
  "sessions:read",
958
1198
  "sessions:control",
1199
+ // Sandbox-surfacing (mirror of @opengeni/contracts Permission). stream:view is
1200
+ // strictly broader than sessions:read (un-redacted pixels); stream:control is
1201
+ // the never-granted-v1 raw-input plane; stream:acknowledge is the secret-leak
1202
+ // consent gate.
1203
+ "stream:view",
1204
+ "stream:control",
1205
+ "stream:acknowledge",
959
1206
  "files:upload",
960
1207
  "files:read",
1208
+ "files:write",
1209
+ "terminal:attach",
961
1210
  "documents:manage",
962
1211
  "documents:search",
963
1212
  "scheduled_tasks:manage",
@@ -978,7 +1227,10 @@ var KNOWN_USAGE_EVENT_TYPES = [
978
1227
  "file.deleted",
979
1228
  "document.indexed",
980
1229
  "scheduled_task.fired",
981
- "api_key.request"
1230
+ "api_key.request",
1231
+ // sandbox warm-time metering (P2.1) — mirrors contracts UsageEventType.
1232
+ "sandbox.warm_seconds",
1233
+ "sandbox.warm_cost"
982
1234
  ];
983
1235
  export {
984
1236
  KNOWN_PERMISSIONS,
@@ -987,13 +1239,23 @@ export {
987
1239
  OpenGeniClient,
988
1240
  OpenGeniStreamError,
989
1241
  SESSION_EVENT_TYPES,
1242
+ TTYD_SUBPROTOCOL,
1243
+ TtydClientCommand,
1244
+ TtydServerCommand,
1245
+ applyUrlRotation,
1246
+ desktopSocketUrl,
990
1247
  formatSseEvent,
991
1248
  isRetryableStreamError,
1249
+ nextDesktopState,
992
1250
  parseSseStream,
993
1251
  proxySessionEventStream,
994
1252
  resumeSequenceFromRequest,
995
1253
  sessionEventsToSseResponse,
996
1254
  sessionEventsToSseStream,
997
- streamSessionEvents
1255
+ streamSessionEvents,
1256
+ terminalSocketUrl,
1257
+ ttydAuthFrame,
1258
+ ttydInputFrame,
1259
+ ttydResizeFrame
998
1260
  };
999
1261
  //# sourceMappingURL=index.js.map