@2018kguo/jumpseat 0.1.0 → 0.2.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/README.md CHANGED
@@ -5,12 +5,12 @@ computer-use agent's Linux desktop live (H.264 over one websocket, decoded with
5
5
  WebCodecs) and take over when it needs you. No dependencies.
6
6
 
7
7
  ```ts
8
- import { connect, supportsStream } from "@2018kguo/jumpseat";
8
+ import { connect } from "@2018kguo/jumpseat";
9
9
 
10
- if (!(await supportsStream())) showFallback(); // no WebCodecs H.264 decoder
11
10
  const view = connect(element, `wss://host/stream?token=${token}`, {
12
11
  connected: () => {},
13
12
  dropped: () => {},
13
+ unsupported: () => {}, // no WebCodecs H.264 decoder in this browser
14
14
  status: ({ kind, text }) => {}, // "acting" | "waiting" | "handoff" | "idle"
15
15
  });
16
16
  view.setControlling(true); // the person's keys, pointer and scrolls reach the desktop
@@ -3,13 +3,6 @@
3
3
  * WebCodecs onto a canvas, and the user's input sent back while they have
4
4
  * taken over. The wire format is in docs/reference.md.
5
5
  */
6
- /** The desktop's size until the server says otherwise. */
7
- export declare const DESKTOP_WIDTH = 1280;
8
- export declare const DESKTOP_HEIGHT = 800;
9
- type Size = {
10
- height: number;
11
- width: number;
12
- };
13
6
  /** What the agent is doing on the desktop, as the VM reports it. */
14
7
  export type AgentStatus = {
15
8
  kind: "idle" | "acting" | "waiting" | "handoff";
@@ -19,36 +12,16 @@ export type DesktopStream = {
19
12
  close(): void;
20
13
  setControlling(controlling: boolean): void;
21
14
  };
22
- type Rect = {
23
- height: number;
24
- left: number;
25
- top: number;
26
- width: number;
27
- };
28
- /**
29
- * The desktop pixel under a client point, or null outside the picture. The
30
- * canvas is letterboxed (object-fit: contain), so the picture is the largest
31
- * box of the desktop's size centered in the element.
32
- */
33
- export declare function desktopPoint(rect: Rect, clientX: number, clientY: number, size?: Size): {
34
- x: number;
35
- y: number;
36
- } | null;
37
- /**
38
- * The key the VM gets for a KeyboardEvent.key. Meta is Control there, so
39
- * Cmd+A, Cmd+L and Cmd+C on a Mac do what they do at home.
40
- */
41
- export declare function desktopKey(event: Pick<KeyboardEvent, "altKey" | "code" | "ctrlKey" | "key" | "metaKey">): string;
42
- /** Whether this browser can decode the stream (WebCodecs H.264). */
43
- export declare function canDecodeDesktop(): boolean;
44
- /** The codec the server sends: H.264 High profile (CABAC, no B-frames). */
45
- export declare const STREAM_CODEC = "avc1.640029";
46
15
  /**
47
16
  * Whether this browser decodes the stream: WebCodecs exists and has an H.264
48
17
  * decoder (some Linux Chromium builds ship without one).
49
18
  */
50
19
  export declare function supportsStream(): Promise<boolean>;
51
- export declare function connectDesktopStream(container: HTMLElement, socketUrl: string, on: {
20
+ /**
21
+ * Streams the desktop at socketUrl into container, which it fills. A browser
22
+ * that cannot decode the stream gets on.unsupported (or on.dropped) instead.
23
+ */
24
+ export declare function connect(container: HTMLElement, socketUrl: string, on: {
52
25
  connected: () => void;
53
26
  dropped: () => void;
54
27
  status?: (status: AgentStatus) => void;
@@ -62,6 +35,3 @@ export declare function connectDesktopStream(container: HTMLElement, socketUrl:
62
35
  options?: {
63
36
  resize?: boolean;
64
37
  }): DesktopStream;
65
- /** connect(container, url, handlers): the same as connectDesktopStream. */
66
- export declare const connect: typeof connectDesktopStream;
67
- export {};
package/dist/jumpseat.js CHANGED
@@ -59,12 +59,9 @@ var HEARTBEAT_MS = 5000;
59
59
  var FIRST_FRAME_MS = 20000;
60
60
  var X_BUTTONS = { 0: 1, 1: 2, 2: 3 };
61
61
  var POINTER_ARROW = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='20'%3E%3Cpath d='M1 1v15l4-4 3 7 3-1-3-7h6z' fill='white' stroke='black'/%3E%3C/svg%3E")`;
62
- function canDecodeDesktop() {
63
- return typeof window !== "undefined" && "VideoDecoder" in window;
64
- }
65
62
  var STREAM_CODEC = "avc1.640029";
66
63
  async function supportsStream() {
67
- if (!canDecodeDesktop()) {
64
+ if (typeof VideoDecoder === "undefined") {
68
65
  return false;
69
66
  }
70
67
  try {
@@ -73,7 +70,11 @@ async function supportsStream() {
73
70
  return false;
74
71
  }
75
72
  }
76
- function connectDesktopStream(container, socketUrl, on, options = {}) {
73
+ function connect(container, socketUrl, on, options = {}) {
74
+ if (typeof VideoDecoder === "undefined") {
75
+ setTimeout(on.unsupported ?? on.dropped);
76
+ return { close() {}, setControlling() {} };
77
+ }
77
78
  const canvas = document.createElement("canvas");
78
79
  const size = { height: DESKTOP_HEIGHT, width: DESKTOP_WIDTH };
79
80
  canvas.width = size.width;
@@ -109,23 +110,18 @@ function connectDesktopStream(container, socketUrl, on, options = {}) {
109
110
  });
110
111
  const socket = new WebSocket(socketUrl);
111
112
  socket.binaryType = "arraybuffer";
112
- async function checkDecoderSupport() {
113
- if (!await supportsStream() && !closed) {
114
- closed = true;
115
- socket.close();
116
- (on.unsupported ?? on.dropped)();
117
- }
118
- }
119
- function drop() {
113
+ function stop(notify) {
120
114
  if (!closed) {
121
115
  closed = true;
122
116
  clearInterval(heartbeat);
123
117
  clearTimeout(firstFrame);
124
118
  resizer?.disconnect();
125
119
  socket.close();
126
- on.dropped();
120
+ notify();
127
121
  }
128
122
  }
123
+ const drop = () => stop(on.dropped);
124
+ supportsStream().then((ok) => ok || stop(on.unsupported ?? on.dropped));
129
125
  const sendAlways = (message) => {
130
126
  if (socket.readyState === WebSocket.OPEN) {
131
127
  socket.send(JSON.stringify(message));
@@ -193,7 +189,6 @@ function connectDesktopStream(container, socketUrl, on, options = {}) {
193
189
  if (!key) {
194
190
  return;
195
191
  }
196
- checkDecoderSupport();
197
192
  decoder.configure({
198
193
  codec: STREAM_CODEC,
199
194
  optimizeForLatency: true
@@ -448,15 +443,7 @@ function desktopPerCssPixel(canvas) {
448
443
  const rect = canvas.getBoundingClientRect();
449
444
  return 1 / Math.min(rect.width / canvas.width, rect.height / canvas.height);
450
445
  }
451
- var connect = connectDesktopStream;
452
446
  export {
453
- DESKTOP_HEIGHT,
454
- DESKTOP_WIDTH,
455
- STREAM_CODEC,
456
- canDecodeDesktop,
457
447
  connect,
458
- connectDesktopStream,
459
- desktopKey,
460
- desktopPoint,
461
448
  supportsStream
462
449
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2018kguo/jumpseat",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Watch and take over a computer-use agent's desktop: the browser client for jumpseat (H.264 over a websocket, WebCodecs).",
5
5
  "keywords": [
6
6
  "computer-use",