@authloop-ai/core 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AuthLoop
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @authloop-ai/core
2
+
3
+ Core engine for [AuthLoop](https://authloop.ai) — CDP screencast, end-to-end encryption, and WebSocket relay.
4
+
5
+ This package powers the MCP server and OpenClaw plugin. You typically don't use it directly — use [`@authloop-ai/mcp`](../mcp) or [`@authloop-ai/openclaw-authloop`](../openclaw-plugin) instead.
6
+
7
+ ## What it does
8
+
9
+ - **CDP client** — connects to a Chromium browser via Chrome DevTools Protocol, captures screencast frames (JPEG), dispatches keystrokes and input events
10
+ - **E2EE** — ECDH P-256 key exchange + AES-256-GCM encryption for all user input between the human's browser and the agent's machine
11
+ - **BrowserStream** — WebSocket relay that streams CDP frames to the human and relays encrypted input back to the browser
12
+ - **Session lifecycle** — creates sessions via the AuthLoop API, manages the PENDING → ACTIVE → RESOLVED state machine, handles cleanup on disconnect
13
+
14
+ ## Exports
15
+
16
+ ```ts
17
+ // Session management
18
+ export { startSession, waitForStatus, stopSession } from "./session.js";
19
+ export type { ToHumanInput, StartSessionOutput, SessionStatusOutput, SessionStatus };
20
+
21
+ // CDP screencast + input dispatch
22
+ export { BrowserStream } from "./stream.js";
23
+ export { CdpClient } from "./cdp.js";
24
+
25
+ // End-to-end encryption
26
+ export { E2EESession } from "./crypto.js";
27
+ ```
28
+
29
+ ## When to use this directly
30
+
31
+ Only if you're building a custom integration that isn't covered by the MCP server, OpenClaw plugin, or SDK. For example:
32
+
33
+ - A custom agent runtime that needs direct control over the screencast stream
34
+ - A browser extension that manages its own CDP connection
35
+ - A relay server with custom transport (e.g., WebRTC instead of WebSocket)
36
+
37
+ For most use cases, use the higher-level packages instead.
38
+
39
+ ## License
40
+
41
+ MIT
package/dist/cdp.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Thin CDP WebSocket client. Uses native WebSocket (Node 22+).
3
+ * Supports both direct WebSocket URLs (ws://, wss://) and HTTP-based CDP
4
+ * endpoints (http://, https://) — auto-discovers the WebSocket debugger URL
5
+ * via /json/version for HTTP endpoints.
6
+ * No auto-reconnect — a CDP drop means the session is dead.
7
+ */
8
+ type EventHandler = (params: Record<string, unknown>) => void;
9
+ export declare class CdpClient {
10
+ private cdpUrl;
11
+ private ws;
12
+ private nextId;
13
+ private pending;
14
+ private listeners;
15
+ private closeHandlers;
16
+ private closed;
17
+ constructor(cdpUrl: string);
18
+ /** Register a callback for when the CDP connection closes */
19
+ onClose(handler: () => void): void;
20
+ connect(): Promise<void>;
21
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
22
+ on(event: string, handler: EventHandler): void;
23
+ close(): void;
24
+ }
25
+ export {};
package/dist/cdp.js ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Thin CDP WebSocket client. Uses native WebSocket (Node 22+).
3
+ * Supports both direct WebSocket URLs (ws://, wss://) and HTTP-based CDP
4
+ * endpoints (http://, https://) — auto-discovers the WebSocket debugger URL
5
+ * via /json/version for HTTP endpoints.
6
+ * No auto-reconnect — a CDP drop means the session is dead.
7
+ */
8
+ import createDebug from "debug";
9
+ const debug = createDebug("authloop:cdp");
10
+ /**
11
+ * Resolves a CDP URL to a page-level WebSocket URL.
12
+ * - ws:// / wss:// URLs are returned as-is (assumed to be a page target).
13
+ * - http:// / https:// URLs are treated as CDP HTTP endpoints — we call
14
+ * /json to find the first "page" target and use its webSocketDebuggerUrl.
15
+ * Falls back to /json/version (browser-level) if no page targets exist.
16
+ */
17
+ async function resolveWebSocketUrl(cdpUrl) {
18
+ if (cdpUrl.startsWith("ws://") || cdpUrl.startsWith("wss://")) {
19
+ debug("CDP URL is already WebSocket: %s", cdpUrl);
20
+ return cdpUrl;
21
+ }
22
+ const base = cdpUrl.replace(/\/+$/, "");
23
+ // Try /json first to get a page-level target (required for Page.startScreencast)
24
+ try {
25
+ const listUrl = `${base}/json`;
26
+ debug("discovering page targets from %s", listUrl);
27
+ const res = await fetch(listUrl);
28
+ if (res.ok) {
29
+ const targets = (await res.json());
30
+ // Filter to page targets with WebSocket URLs
31
+ const pages = targets.filter((t) => t.type === "page" && t.webSocketDebuggerUrl);
32
+ // Prefer real web pages over chrome internal pages (about:, chrome:, chrome-extension:)
33
+ const webPage = pages.find((t) => t.url && /^https?:\/\//.test(t.url));
34
+ const page = webPage ?? pages[0];
35
+ if (page) {
36
+ debug("discovered page target: %s (%s)", page.title ?? page.url, page.webSocketDebuggerUrl);
37
+ return page.webSocketDebuggerUrl;
38
+ }
39
+ debug("no page targets found, falling back to /json/version");
40
+ }
41
+ }
42
+ catch {
43
+ debug("/json failed, falling back to /json/version");
44
+ }
45
+ // Fallback: browser-level endpoint
46
+ const versionUrl = `${base}/json/version`;
47
+ debug("discovering WebSocket URL from %s", versionUrl);
48
+ const res = await fetch(versionUrl);
49
+ if (!res.ok) {
50
+ throw new Error(`CDP discovery failed: ${versionUrl} returned ${res.status}`);
51
+ }
52
+ const data = (await res.json());
53
+ if (!data.webSocketDebuggerUrl) {
54
+ throw new Error(`CDP discovery: no webSocketDebuggerUrl in ${versionUrl} response`);
55
+ }
56
+ debug("discovered browser WebSocket URL: %s", data.webSocketDebuggerUrl);
57
+ return data.webSocketDebuggerUrl;
58
+ }
59
+ export class CdpClient {
60
+ cdpUrl;
61
+ ws = null;
62
+ nextId = 1;
63
+ pending = new Map();
64
+ listeners = new Map();
65
+ closeHandlers = [];
66
+ closed = false;
67
+ constructor(cdpUrl) {
68
+ this.cdpUrl = cdpUrl;
69
+ }
70
+ /** Register a callback for when the CDP connection closes */
71
+ onClose(handler) {
72
+ this.closeHandlers.push(handler);
73
+ }
74
+ async connect() {
75
+ const wsUrl = await resolveWebSocketUrl(this.cdpUrl);
76
+ debug("connecting to %s", wsUrl);
77
+ return new Promise((resolve, reject) => {
78
+ const ws = new WebSocket(wsUrl);
79
+ this.ws = ws;
80
+ ws.addEventListener("open", () => {
81
+ debug("connected");
82
+ resolve();
83
+ });
84
+ ws.addEventListener("error", (e) => {
85
+ if (!this.closed) {
86
+ debug("connection error: %s", e.message ?? "unknown");
87
+ reject(new Error(`CDP connection error: ${e.message ?? "unknown"}`));
88
+ }
89
+ });
90
+ ws.addEventListener("message", (event) => {
91
+ const data = JSON.parse(String(event.data));
92
+ // Response to a command
93
+ if (data.id !== undefined) {
94
+ const pending = this.pending.get(data.id);
95
+ if (pending) {
96
+ this.pending.delete(data.id);
97
+ if (data.error) {
98
+ debug("command %d error: %s", data.id, data.error.message);
99
+ pending.reject(new Error(`CDP error: ${data.error.message}`));
100
+ }
101
+ else {
102
+ debug("command %d ok", data.id);
103
+ pending.resolve(data.result);
104
+ }
105
+ }
106
+ return;
107
+ }
108
+ // Event
109
+ if (data.method) {
110
+ debug("event: %s", data.method);
111
+ const handlers = this.listeners.get(data.method);
112
+ if (handlers) {
113
+ for (const handler of handlers) {
114
+ handler(data.params ?? {});
115
+ }
116
+ }
117
+ }
118
+ });
119
+ ws.addEventListener("close", () => {
120
+ debug("connection closed, rejecting %d pending calls", this.pending.size);
121
+ this.closed = true;
122
+ for (const [, pending] of this.pending) {
123
+ pending.reject(new Error("CDP connection closed"));
124
+ }
125
+ this.pending.clear();
126
+ for (const handler of this.closeHandlers) {
127
+ handler();
128
+ }
129
+ });
130
+ });
131
+ }
132
+ send(method, params) {
133
+ if (!this.ws || this.closed) {
134
+ return Promise.reject(new Error("CDP not connected"));
135
+ }
136
+ const id = this.nextId++;
137
+ debug("send #%d %s", id, method);
138
+ return new Promise((resolve, reject) => {
139
+ this.pending.set(id, { resolve, reject });
140
+ this.ws.send(JSON.stringify({ id, method, params }));
141
+ });
142
+ }
143
+ on(event, handler) {
144
+ let set = this.listeners.get(event);
145
+ if (!set) {
146
+ set = new Set();
147
+ this.listeners.set(event, set);
148
+ }
149
+ set.add(handler);
150
+ }
151
+ close() {
152
+ debug("closing, %d pending calls", this.pending.size);
153
+ this.closed = true;
154
+ for (const [, pending] of this.pending) {
155
+ pending.reject(new Error("CDP client closed"));
156
+ }
157
+ this.pending.clear();
158
+ this.ws?.close();
159
+ this.ws = null;
160
+ }
161
+ }
162
+ //# sourceMappingURL=cdp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp.js","sourceRoot":"","sources":["../src/cdp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * End-to-end encryption for the keystroke relay.
3
+ *
4
+ * Uses ECDH (P-256) key exchange + AES-256-GCM.
5
+ * The relay only sees ciphertext — it cannot read passwords or OTPs.
6
+ *
7
+ * Flow:
8
+ * 1. MCP generates ECDH P-256 keypair, sends public key to viewer via relay
9
+ * 2. Viewer generates ECDH P-256 keypair, sends public key to MCP via relay
10
+ * 3. Both sides derive the same shared secret using ECDH
11
+ * 4. Viewer encrypts input events with AES-256-GCM before sending
12
+ * 5. MCP decrypts input events after receiving
13
+ * 6. Frames (screenshots) are NOT encrypted — they show visible page content only
14
+ */
15
+ export declare class E2EESession {
16
+ private ecdh;
17
+ private sharedSecret;
18
+ private _publicKey;
19
+ constructor();
20
+ /** Our public key to send to the viewer */
21
+ get publicKey(): string;
22
+ /** Derive shared secret from the viewer's public key */
23
+ deriveSecret(viewerPublicKey: string): void;
24
+ /** Check if key exchange is complete */
25
+ get ready(): boolean;
26
+ /** Decrypt a message from the viewer */
27
+ decrypt(encrypted: {
28
+ iv: string;
29
+ ciphertext: string;
30
+ tag: string;
31
+ }): string;
32
+ /** Encrypt a message to the viewer (for testing / future use) */
33
+ encrypt(plaintext: string): {
34
+ iv: string;
35
+ ciphertext: string;
36
+ tag: string;
37
+ };
38
+ }
package/dist/crypto.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * End-to-end encryption for the keystroke relay.
3
+ *
4
+ * Uses ECDH (P-256) key exchange + AES-256-GCM.
5
+ * The relay only sees ciphertext — it cannot read passwords or OTPs.
6
+ *
7
+ * Flow:
8
+ * 1. MCP generates ECDH P-256 keypair, sends public key to viewer via relay
9
+ * 2. Viewer generates ECDH P-256 keypair, sends public key to MCP via relay
10
+ * 3. Both sides derive the same shared secret using ECDH
11
+ * 4. Viewer encrypts input events with AES-256-GCM before sending
12
+ * 5. MCP decrypts input events after receiving
13
+ * 6. Frames (screenshots) are NOT encrypted — they show visible page content only
14
+ */
15
+ import { createECDH, createDecipheriv, createCipheriv, randomBytes } from "node:crypto";
16
+ import createDebug from "debug";
17
+ const debug = createDebug("authloop:crypto");
18
+ export class E2EESession {
19
+ ecdh = createECDH("prime256v1");
20
+ sharedSecret = null;
21
+ _publicKey;
22
+ constructor() {
23
+ this.ecdh.generateKeys();
24
+ this._publicKey = this.ecdh.getPublicKey("base64");
25
+ debug("generated keypair, public key: %s...", this._publicKey.slice(0, 20));
26
+ }
27
+ /** Our public key to send to the viewer */
28
+ get publicKey() {
29
+ return this._publicKey;
30
+ }
31
+ /** Derive shared secret from the viewer's public key */
32
+ deriveSecret(viewerPublicKey) {
33
+ const secret = this.ecdh.computeSecret(Buffer.from(viewerPublicKey, "base64"));
34
+ // Use first 32 bytes as AES-256 key
35
+ this.sharedSecret = secret.subarray(0, 32);
36
+ debug("shared secret derived");
37
+ }
38
+ /** Check if key exchange is complete */
39
+ get ready() {
40
+ return this.sharedSecret !== null;
41
+ }
42
+ /** Decrypt a message from the viewer */
43
+ decrypt(encrypted) {
44
+ if (!this.sharedSecret)
45
+ throw new Error("E2EE not ready — key exchange incomplete");
46
+ const iv = Buffer.from(encrypted.iv, "base64");
47
+ const ciphertext = Buffer.from(encrypted.ciphertext, "base64");
48
+ const tag = Buffer.from(encrypted.tag, "base64");
49
+ const decipher = createDecipheriv("aes-256-gcm", this.sharedSecret, iv);
50
+ decipher.setAuthTag(tag);
51
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
52
+ return decrypted.toString("utf8");
53
+ }
54
+ /** Encrypt a message to the viewer (for testing / future use) */
55
+ encrypt(plaintext) {
56
+ if (!this.sharedSecret)
57
+ throw new Error("E2EE not ready — key exchange incomplete");
58
+ const iv = randomBytes(12);
59
+ const cipher = createCipheriv("aes-256-gcm", this.sharedSecret, iv);
60
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
61
+ const tag = cipher.getAuthTag();
62
+ return {
63
+ iv: iv.toString("base64"),
64
+ ciphertext: ciphertext.toString("base64"),
65
+ tag: tag.toString("base64"),
66
+ };
67
+ }
68
+ }
69
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC,MAAM,KAAK,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;AAE7C,MAAM,OAAO,WAAW;IACd,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAChC,YAAY,GAAkB,IAAI,CAAC;IACnC,UAAU,CAAS;IAE3B;QACE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACnD,KAAK,CAAC,sCAAsC,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,2CAA2C;IAC3C,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,wDAAwD;IACxD,YAAY,CAAC,eAAuB;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/E,oCAAoC;QACpC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3C,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACjC,CAAC;IAED,wCAAwC;IACxC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;IACpC,CAAC;IAED,wCAAwC;IACxC,OAAO,CAAC,SAA0D;QAChE,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAEpF,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEjD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACxE,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAEzB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACjF,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,iEAAiE;IACjE,OAAO,CAAC,SAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAEpF,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAEpE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAEhC,OAAO;YACL,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACzB,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACzC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC5B,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export { startSession, waitForStatus, stopSession, _resetActiveSession, _getActiveSession, type ToHumanInput, type StartSessionOutput, type SessionStatusOutput, type SessionStatus, } from "./session.js";
2
+ export { BrowserStream, type StreamResult } from "./stream.js";
3
+ export { CdpClient } from "./cdp.js";
4
+ export { E2EESession } from "./crypto.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { startSession, waitForStatus, stopSession, _resetActiveSession, _getActiveSession, } from "./session.js";
2
+ export { BrowserStream } from "./stream.js";
3
+ export { CdpClient } from "./cdp.js";
4
+ export { E2EESession } from "./crypto.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,iBAAiB,GAKlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAqB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Session lifecycle: startSession → (agent sends URL) → waitForStatus → resolved → cleanup
3
+ *
4
+ * startSession() creates the session, connects WebSocket, starts CDP screencast in the
5
+ * background, and returns immediately with the session_url for the agent to send to the human.
6
+ *
7
+ * waitForStatus() blocks until the session reaches a terminal state (resolved/cancelled/timeout/error).
8
+ *
9
+ * stopSession() cleans up the stream, WebSocket, and CDP connection.
10
+ */
11
+ import { AuthLoop } from "@authloop-ai/sdk";
12
+ import { BrowserStream, type StreamResult } from "./stream.js";
13
+ export interface ToHumanInput {
14
+ service: string;
15
+ cdpUrl: string;
16
+ context?: {
17
+ url?: string;
18
+ blockerType?: "otp" | "password" | "captcha" | "security_question" | "document_upload" | "other";
19
+ hint?: string;
20
+ };
21
+ }
22
+ export interface StartSessionOutput {
23
+ sessionId: string;
24
+ sessionUrl: string;
25
+ }
26
+ export type SessionStatus = "streaming" | StreamResult;
27
+ export interface SessionStatusOutput {
28
+ sessionId: string;
29
+ sessionUrl: string;
30
+ status: SessionStatus;
31
+ }
32
+ interface ActiveSession {
33
+ sessionId: string;
34
+ sessionUrl: string;
35
+ status: SessionStatus;
36
+ authloop: AuthLoop;
37
+ stream: BrowserStream | null;
38
+ ws: WebSocket;
39
+ cdpUrl: string;
40
+ startTime: number;
41
+ /** Resolves when status changes from "streaming" to a terminal state */
42
+ onTerminal: Promise<StreamResult>;
43
+ resolveTerminal: ((result: StreamResult) => void) | null;
44
+ }
45
+ /** @internal — exposed for testing only */
46
+ export declare function _resetActiveSession(): void;
47
+ /** @internal — exposed for testing only */
48
+ export declare function _getActiveSession(): ActiveSession | null;
49
+ /**
50
+ * Start a session: create via API, connect WebSocket, start CDP screencast in background.
51
+ * Returns immediately with session_url for the agent to send to the human.
52
+ */
53
+ export declare function startSession(authloop: AuthLoop, options: ToHumanInput): Promise<StartSessionOutput>;
54
+ /**
55
+ * Block until the session reaches a terminal state.
56
+ * Returns null if no session is active.
57
+ * Auto-cleans up after returning.
58
+ */
59
+ export declare function waitForStatus(): Promise<SessionStatusOutput | null>;
60
+ /**
61
+ * Stop the active session and clean up all resources.
62
+ */
63
+ export declare function stopSession(): Promise<void>;
64
+ export {};
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Session lifecycle: startSession → (agent sends URL) → waitForStatus → resolved → cleanup
3
+ *
4
+ * startSession() creates the session, connects WebSocket, starts CDP screencast in the
5
+ * background, and returns immediately with the session_url for the agent to send to the human.
6
+ *
7
+ * waitForStatus() blocks until the session reaches a terminal state (resolved/cancelled/timeout/error).
8
+ *
9
+ * stopSession() cleans up the stream, WebSocket, and CDP connection.
10
+ */
11
+ import createDebug from "debug";
12
+ import { BrowserStream } from "./stream.js";
13
+ const debug = createDebug("authloop:session");
14
+ const perf = createDebug("authloop:perf");
15
+ let active = null;
16
+ /** @internal — exposed for testing only */
17
+ export function _resetActiveSession() {
18
+ active = null;
19
+ }
20
+ /** @internal — exposed for testing only */
21
+ export function _getActiveSession() {
22
+ return active;
23
+ }
24
+ /**
25
+ * Transition the active session to a terminal status.
26
+ * Resolves the terminal promise so waitForStatus() unblocks.
27
+ */
28
+ function setTerminalStatus(result) {
29
+ if (!active || active.status !== "streaming")
30
+ return;
31
+ debug("terminal status: %s", result);
32
+ active.status = result;
33
+ active.resolveTerminal?.(result);
34
+ active.resolveTerminal = null;
35
+ }
36
+ /**
37
+ * Start a session: create via API, connect WebSocket, start CDP screencast in background.
38
+ * Returns immediately with session_url for the agent to send to the human.
39
+ */
40
+ export async function startSession(authloop, options) {
41
+ if (active) {
42
+ throw new Error("A session is already in progress");
43
+ }
44
+ debug("startSession: service=%s", options.service);
45
+ const startTime = Date.now();
46
+ // 1. Create session via SDK
47
+ const session = await authloop.toHuman({
48
+ service: options.service,
49
+ cdpUrl: options.cdpUrl,
50
+ context: options.context,
51
+ });
52
+ debug("session created: id=%s url=%s", session.sessionId, session.sessionUrl);
53
+ perf("[perf:session] session created: %dms", Date.now() - startTime);
54
+ // 2. Connect WebSocket immediately
55
+ const wsUrl = `${session.streamUrl}?token=${encodeURIComponent(session.streamToken)}&role=agent`;
56
+ debug("connecting to relay WebSocket");
57
+ const wsConnectStart = Date.now();
58
+ const ws = await new Promise((resolve, reject) => {
59
+ const socket = new WebSocket(wsUrl);
60
+ const timeout = setTimeout(() => {
61
+ socket.close();
62
+ reject(new Error("WebSocket connection timed out after 15s"));
63
+ }, 15000);
64
+ socket.addEventListener("open", () => {
65
+ clearTimeout(timeout);
66
+ resolve(socket);
67
+ });
68
+ socket.addEventListener("error", (e) => {
69
+ clearTimeout(timeout);
70
+ reject(new Error(`WebSocket error: ${e.message ?? "connection failed"}`));
71
+ });
72
+ });
73
+ perf("[perf:stream] WebSocket connect: %dms", Date.now() - wsConnectStart);
74
+ debug("relay connected");
75
+ // 3. Set up terminal promise
76
+ let resolveTerminal = null;
77
+ const onTerminal = new Promise((resolve) => {
78
+ resolveTerminal = resolve;
79
+ });
80
+ // 4. Set up active session
81
+ active = {
82
+ sessionId: session.sessionId,
83
+ sessionUrl: session.sessionUrl,
84
+ status: "streaming",
85
+ authloop,
86
+ stream: null,
87
+ ws,
88
+ cdpUrl: options.cdpUrl,
89
+ startTime,
90
+ onTerminal,
91
+ resolveTerminal,
92
+ };
93
+ // 5. Listen for events in background
94
+ ws.addEventListener("message", (event) => {
95
+ if (!active || typeof event.data !== "string")
96
+ return;
97
+ try {
98
+ const msg = JSON.parse(event.data);
99
+ if (msg.type === "viewer_connected" && !active.stream) {
100
+ debug("viewer connected, starting browser stream");
101
+ startStreaming().catch((err) => {
102
+ debug("failed to start streaming: %s", err.message);
103
+ setTerminalStatus("error");
104
+ });
105
+ }
106
+ else if (msg.type === "session_expired") {
107
+ debug("session expired");
108
+ setTerminalStatus("timeout");
109
+ }
110
+ else if (msg.type === "session_cancelled") {
111
+ debug("session cancelled");
112
+ setTerminalStatus("cancelled");
113
+ }
114
+ }
115
+ catch {
116
+ // ignore parse errors
117
+ }
118
+ });
119
+ ws.addEventListener("close", () => {
120
+ if (active && active.status === "streaming") {
121
+ debug("relay WebSocket closed unexpectedly");
122
+ setTerminalStatus("error");
123
+ }
124
+ });
125
+ perf("[perf:session] startSession total: %dms", Date.now() - startTime);
126
+ return {
127
+ sessionId: session.sessionId,
128
+ sessionUrl: session.sessionUrl,
129
+ };
130
+ }
131
+ /**
132
+ * Start CDP screencast and wire up resolution events.
133
+ * Called automatically when viewer connects.
134
+ */
135
+ async function startStreaming() {
136
+ if (!active)
137
+ return;
138
+ const stream = new BrowserStream({
139
+ ws: active.ws,
140
+ cdpUrl: active.cdpUrl,
141
+ });
142
+ active.stream = stream;
143
+ await stream.start();
144
+ debug("browser stream started");
145
+ // Wait for resolution in background
146
+ stream.waitForResolution().then(async (result) => {
147
+ if (!active)
148
+ return;
149
+ debug("stream result: %s", result);
150
+ // Tell the API the outcome
151
+ if (result === "resolved") {
152
+ debug("resolving session %s", active.sessionId);
153
+ await active.authloop.resolveSession(active.sessionId).catch(() => { });
154
+ }
155
+ else if (result === "cancelled") {
156
+ debug("cancelling session %s", active.sessionId);
157
+ await active.authloop.cancelSession(active.sessionId).catch(() => { });
158
+ }
159
+ setTerminalStatus(result);
160
+ });
161
+ }
162
+ /**
163
+ * Block until the session reaches a terminal state.
164
+ * Returns null if no session is active.
165
+ * Auto-cleans up after returning.
166
+ */
167
+ export async function waitForStatus() {
168
+ if (!active)
169
+ return null;
170
+ debug("waitForStatus: waiting for terminal status...");
171
+ // If already terminal, return immediately
172
+ if (active.status !== "streaming") {
173
+ const result = {
174
+ sessionId: active.sessionId,
175
+ sessionUrl: active.sessionUrl,
176
+ status: active.status,
177
+ };
178
+ debug("waitForStatus: already terminal=%s", active.status);
179
+ await cleanup();
180
+ return result;
181
+ }
182
+ // Block until terminal
183
+ const terminalResult = await active.onTerminal;
184
+ // active may have been cleared by stopSession() during the wait
185
+ if (!active)
186
+ return null;
187
+ const result = {
188
+ sessionId: active.sessionId,
189
+ sessionUrl: active.sessionUrl,
190
+ status: terminalResult,
191
+ };
192
+ debug("waitForStatus: resolved with status=%s", terminalResult);
193
+ await cleanup();
194
+ return result;
195
+ }
196
+ /**
197
+ * Stop the active session and clean up all resources.
198
+ */
199
+ export async function stopSession() {
200
+ if (!active)
201
+ return;
202
+ debug("stopSession: sessionId=%s", active.sessionId);
203
+ // Cancel on the API if still streaming
204
+ if (active.status === "streaming") {
205
+ await active.authloop.cancelSession(active.sessionId).catch(() => { });
206
+ }
207
+ // Resolve the terminal promise so waitForStatus() unblocks
208
+ setTerminalStatus("cancelled");
209
+ await cleanup();
210
+ }
211
+ async function cleanup() {
212
+ if (!active)
213
+ return;
214
+ const session = active;
215
+ active = null;
216
+ await session.stream?.stop();
217
+ if (!session.stream) {
218
+ session.ws.close();
219
+ }
220
+ perf("[perf:session] session duration: %ds", Math.round((Date.now() - session.startTime) / 1000));
221
+ debug("session cleaned up");
222
+ }
223
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * WebSocket + CDP bridge.
3
+ * Captures browser screencast frames via CDP, sends them as binary JPEG over WebSocket.
4
+ * Receives input events (clicks, keystrokes, scroll) from the human via WebSocket,
5
+ * dispatches them to the browser via CDP.
6
+ *
7
+ * The WebSocket is pre-connected by session.ts and passed in — this class does not
8
+ * manage the WebSocket connection lifecycle.
9
+ */
10
+ export type StreamResult = "resolved" | "cancelled" | "error" | "timeout";
11
+ export declare class BrowserStream {
12
+ private opts;
13
+ private ws;
14
+ private cdp;
15
+ private e2ee;
16
+ private resolveWait;
17
+ private stopped;
18
+ private frameCount;
19
+ private lastFrameData;
20
+ private startTime;
21
+ private firstFrameSent;
22
+ constructor(opts: {
23
+ ws: WebSocket;
24
+ cdpUrl: string;
25
+ });
26
+ start(): Promise<void>;
27
+ waitForResolution(): Promise<StreamResult>;
28
+ stop(): Promise<void>;
29
+ private handleMessage;
30
+ private dispatchMouseClick;
31
+ private dispatchKeyEvent;
32
+ /** Convert modifier flags to CDP bitmask: Alt=1, Ctrl=2, Meta=4, Shift=8 */
33
+ private getModifiers;
34
+ private dispatchPaste;
35
+ private dispatchScroll;
36
+ }
package/dist/stream.js ADDED
@@ -0,0 +1,321 @@
1
+ /**
2
+ * WebSocket + CDP bridge.
3
+ * Captures browser screencast frames via CDP, sends them as binary JPEG over WebSocket.
4
+ * Receives input events (clicks, keystrokes, scroll) from the human via WebSocket,
5
+ * dispatches them to the browser via CDP.
6
+ *
7
+ * The WebSocket is pre-connected by session.ts and passed in — this class does not
8
+ * manage the WebSocket connection lifecycle.
9
+ */
10
+ import createDebug from "debug";
11
+ import { CdpClient } from "./cdp.js";
12
+ import { E2EESession } from "./crypto.js";
13
+ const debug = createDebug("authloop:stream");
14
+ const perf = createDebug("authloop:perf");
15
+ /** Windows virtual key codes for CDP Input.dispatchKeyEvent */
16
+ const KEY_CODES = {
17
+ Backspace: 8, Tab: 9, Enter: 13, Shift: 16, Control: 17, Alt: 18,
18
+ Escape: 27, " ": 32, PageUp: 33, PageDown: 34, End: 35, Home: 36,
19
+ ArrowLeft: 37, ArrowUp: 38, ArrowRight: 39, ArrowDown: 40,
20
+ Insert: 45, Delete: 46,
21
+ Meta: 91, ContextMenu: 93,
22
+ F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117,
23
+ F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123,
24
+ };
25
+ export class BrowserStream {
26
+ opts;
27
+ ws;
28
+ cdp = null;
29
+ e2ee = new E2EESession();
30
+ resolveWait = null;
31
+ stopped = false;
32
+ frameCount = 0;
33
+ lastFrameData = null;
34
+ startTime = 0;
35
+ firstFrameSent = false;
36
+ constructor(opts) {
37
+ this.opts = opts;
38
+ this.ws = opts.ws;
39
+ }
40
+ async start() {
41
+ this.startTime = Date.now();
42
+ // 1. Connect to CDP
43
+ debug("connecting to CDP: %s", this.opts.cdpUrl);
44
+ let t0 = Date.now();
45
+ this.cdp = new CdpClient(this.opts.cdpUrl);
46
+ this.cdp.onClose(() => {
47
+ if (!this.stopped) {
48
+ debug("CDP disconnected unexpectedly");
49
+ this.resolveWait?.("error");
50
+ }
51
+ });
52
+ await this.cdp.connect();
53
+ perf("[perf:stream] CDP connect: %dms", Date.now() - t0);
54
+ debug("CDP connected");
55
+ // 2. Send our E2EE public key so the viewer can derive the shared secret
56
+ this.ws.send(JSON.stringify({ type: "pubkey", key: this.e2ee.publicKey }));
57
+ debug("sent E2EE public key");
58
+ // 3. Listen for messages from the relay (input events + control)
59
+ this.ws.addEventListener("message", (event) => {
60
+ if (this.stopped)
61
+ return;
62
+ if (typeof event.data === "string") {
63
+ try {
64
+ const msg = JSON.parse(event.data);
65
+ this.handleMessage(msg);
66
+ }
67
+ catch {
68
+ debug("failed to parse message (dropped)");
69
+ }
70
+ }
71
+ });
72
+ this.ws.addEventListener("close", () => {
73
+ if (!this.stopped) {
74
+ debug("relay WebSocket closed unexpectedly");
75
+ this.resolveWait?.("error");
76
+ }
77
+ });
78
+ // 4. Forward CDP screencast frames as binary JPEG with metadata header
79
+ this.cdp.on("Page.screencastFrame", (params) => {
80
+ if (this.stopped)
81
+ return;
82
+ const sessionId = params.sessionId;
83
+ const data = params.data;
84
+ const metadata = params.metadata;
85
+ // ACK so CDP sends the next frame
86
+ this.cdp?.send("Page.screencastFrameAck", { sessionId }).catch(() => { });
87
+ // Send frame with metadata as a JSON message, then binary JPEG
88
+ const jpegBuffer = Buffer.from(data, "base64");
89
+ const metaJson = JSON.stringify({
90
+ type: "frame",
91
+ deviceWidth: metadata?.deviceWidth,
92
+ deviceHeight: metadata?.deviceHeight,
93
+ offsetTop: metadata?.offsetTop ?? 0,
94
+ pageScaleFactor: metadata?.pageScaleFactor ?? 1,
95
+ scrollOffsetX: metadata?.scrollOffsetX ?? 0,
96
+ scrollOffsetY: metadata?.scrollOffsetY ?? 0,
97
+ jpegSize: jpegBuffer.byteLength,
98
+ });
99
+ // Cache latest frame so we can send it when a viewer connects
100
+ this.lastFrameData = { meta: metaJson, jpeg: jpegBuffer };
101
+ if (this.ws.readyState === WebSocket.OPEN) {
102
+ this.ws.send(metaJson);
103
+ this.ws.send(jpegBuffer);
104
+ this.frameCount++;
105
+ if (!this.firstFrameSent) {
106
+ this.firstFrameSent = true;
107
+ const frameTime = Date.now();
108
+ perf("[perf:stream] CDP → first screencast frame: %dms", frameTime - this.startTime);
109
+ perf("[perf:stream] first frame → first relay send: %dms", Date.now() - frameTime);
110
+ }
111
+ if (this.frameCount % 100 === 0) {
112
+ debug("sent %d frames", this.frameCount);
113
+ }
114
+ }
115
+ });
116
+ // 5. Start screencast
117
+ await this.cdp.send("Page.startScreencast", {
118
+ format: "jpeg",
119
+ quality: 60,
120
+ maxWidth: 1280,
121
+ maxHeight: 720,
122
+ everyNthFrame: 1,
123
+ });
124
+ debug("screencast started");
125
+ perf("[perf:stream] total start() time: %dms", Date.now() - this.startTime);
126
+ }
127
+ waitForResolution() {
128
+ return new Promise((resolve) => {
129
+ if (this.stopped) {
130
+ resolve("error");
131
+ return;
132
+ }
133
+ this.resolveWait = resolve;
134
+ });
135
+ }
136
+ async stop() {
137
+ if (this.stopped)
138
+ return;
139
+ this.stopped = true;
140
+ debug("stopping stream (sent %d frames)", this.frameCount);
141
+ perf("[perf:stream] frames published: %d", this.frameCount);
142
+ perf("[perf:stream] session duration: %ds", Math.round((Date.now() - this.startTime) / 1000));
143
+ this.cdp?.send("Page.stopScreencast").catch(() => { });
144
+ this.cdp?.close();
145
+ this.cdp = null;
146
+ this.ws.close();
147
+ debug("stream stopped");
148
+ }
149
+ handleMessage(msg) {
150
+ const type = msg.type;
151
+ // Handle E2EE key exchange
152
+ if (type === "pubkey") {
153
+ debug("received viewer public key, deriving shared secret");
154
+ this.e2ee.deriveSecret(msg.key);
155
+ perf("[perf:stream] E2EE key exchange: %dms", Date.now() - this.startTime);
156
+ return;
157
+ }
158
+ // Handle encrypted messages — decrypt then process as normal
159
+ if (type === "encrypted") {
160
+ if (!this.e2ee.ready) {
161
+ debug("received encrypted message but E2EE not ready, dropping");
162
+ return;
163
+ }
164
+ try {
165
+ const plaintext = this.e2ee.decrypt(msg.payload);
166
+ const decrypted = JSON.parse(plaintext);
167
+ debug("decrypted: %s", decrypted.type);
168
+ this.handleMessage(decrypted);
169
+ }
170
+ catch (err) {
171
+ debug("decryption failed: %s", err.message);
172
+ }
173
+ return;
174
+ }
175
+ // Control messages from the relay (always plaintext)
176
+ switch (type) {
177
+ case "session_expired":
178
+ debug("session expired");
179
+ this.resolveWait?.("timeout");
180
+ return;
181
+ case "session_cancelled":
182
+ debug("session cancelled");
183
+ this.resolveWait?.("cancelled");
184
+ return;
185
+ case "viewer_connected":
186
+ debug("viewer connected");
187
+ if (this.lastFrameData && this.ws.readyState === WebSocket.OPEN) {
188
+ debug("sending cached frame to new viewer");
189
+ this.ws.send(this.lastFrameData.meta);
190
+ this.ws.send(this.lastFrameData.jpeg);
191
+ }
192
+ return;
193
+ case "viewer_disconnected":
194
+ debug("viewer disconnected");
195
+ return;
196
+ }
197
+ // Input events — must be decrypted (no plaintext fallback)
198
+ // These arrive only after E2EE decrypt (recursive handleMessage call)
199
+ switch (type) {
200
+ case "click":
201
+ case "dblclick":
202
+ this.dispatchMouseClick(msg);
203
+ break;
204
+ case "keydown":
205
+ case "keyup":
206
+ case "keypress":
207
+ this.dispatchKeyEvent(msg);
208
+ break;
209
+ case "scroll":
210
+ this.dispatchScroll(msg);
211
+ break;
212
+ case "paste":
213
+ this.dispatchPaste(msg);
214
+ break;
215
+ case "back":
216
+ debug("navigate back");
217
+ this.cdp?.send("Runtime.evaluate", { expression: "history.back()" }).catch(() => { });
218
+ break;
219
+ case "forward":
220
+ debug("navigate forward");
221
+ this.cdp?.send("Runtime.evaluate", { expression: "history.forward()" }).catch(() => { });
222
+ break;
223
+ case "reload":
224
+ debug("reload page");
225
+ this.cdp?.send("Page.reload").catch(() => { });
226
+ break;
227
+ case "resolved":
228
+ debug("viewer marked auth as complete");
229
+ this.resolveWait?.("resolved");
230
+ break;
231
+ case "cancelled":
232
+ debug("viewer cancelled the session");
233
+ this.resolveWait?.("cancelled");
234
+ break;
235
+ default:
236
+ debug("unknown message type: %s (dropped)", type);
237
+ break;
238
+ }
239
+ }
240
+ dispatchMouseClick(msg) {
241
+ if (!this.cdp)
242
+ return;
243
+ const x = msg.x;
244
+ const y = msg.y;
245
+ const button = msg.button || "left";
246
+ const clickCount = msg.type === "dblclick" ? 2 : 1;
247
+ this.cdp
248
+ .send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button, clickCount })
249
+ .then(() => this.cdp?.send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button, clickCount }))
250
+ .catch(() => { });
251
+ }
252
+ dispatchKeyEvent(msg) {
253
+ if (!this.cdp)
254
+ return;
255
+ const type = msg.type;
256
+ const key = msg.key;
257
+ const code = msg.code;
258
+ const modifiers = this.getModifiers(msg);
259
+ const keyCode = key ? KEY_CODES[key] ?? 0 : 0;
260
+ if (type === "keypress") {
261
+ // Printable character — send char only (viewer sends keyup separately)
262
+ const text = key && key.length === 1 ? key : undefined;
263
+ if (text) {
264
+ this.cdp.send("Input.dispatchKeyEvent", {
265
+ type: "char", text, modifiers,
266
+ }).catch(() => { });
267
+ }
268
+ }
269
+ else if (type === "keydown") {
270
+ // Special keys (Backspace, Enter, arrows, etc.) or modified keys (Ctrl+A)
271
+ const isModifiedPrintable = key && key.length === 1 && modifiers > 0;
272
+ const text = isModifiedPrintable ? key : undefined;
273
+ this.cdp.send("Input.dispatchKeyEvent", {
274
+ type: "rawKeyDown", key, code, text, modifiers,
275
+ windowsVirtualKeyCode: keyCode || (key && key.length === 1 ? key.toUpperCase().charCodeAt(0) : 0),
276
+ nativeVirtualKeyCode: keyCode || (key && key.length === 1 ? key.toUpperCase().charCodeAt(0) : 0),
277
+ }).catch(() => { });
278
+ }
279
+ else if (type === "keyup") {
280
+ this.cdp.send("Input.dispatchKeyEvent", {
281
+ type: "keyUp", key, code, modifiers,
282
+ windowsVirtualKeyCode: keyCode || (key && key.length === 1 ? key.toUpperCase().charCodeAt(0) : 0),
283
+ nativeVirtualKeyCode: keyCode || (key && key.length === 1 ? key.toUpperCase().charCodeAt(0) : 0),
284
+ }).catch(() => { });
285
+ }
286
+ }
287
+ /** Convert modifier flags to CDP bitmask: Alt=1, Ctrl=2, Meta=4, Shift=8 */
288
+ getModifiers(msg) {
289
+ let m = 0;
290
+ if (msg.altKey)
291
+ m |= 1;
292
+ if (msg.ctrlKey)
293
+ m |= 2;
294
+ if (msg.metaKey)
295
+ m |= 4;
296
+ if (msg.shiftKey)
297
+ m |= 8;
298
+ return m;
299
+ }
300
+ dispatchPaste(msg) {
301
+ if (!this.cdp)
302
+ return;
303
+ const text = msg.text;
304
+ if (text) {
305
+ debug("paste: %d chars", text.length);
306
+ this.cdp.send("Input.insertText", { text }).catch(() => { });
307
+ }
308
+ }
309
+ dispatchScroll(msg) {
310
+ if (!this.cdp)
311
+ return;
312
+ const x = msg.x;
313
+ const y = msg.y;
314
+ const deltaX = msg.deltaX || 0;
315
+ const deltaY = msg.deltaY || 0;
316
+ this.cdp
317
+ .send("Input.dispatchMouseEvent", { type: "mouseWheel", x, y, deltaX, deltaY })
318
+ .catch(() => { });
319
+ }
320
+ }
321
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@authloop-ai/core",
3
+ "version": "0.2.0",
4
+ "description": "Core engine for AuthLoop — CDP screencast, E2EE, WebSocket relay",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "AuthLoop <hello@authloop.ai> (https://authloop.ai)",
8
+ "homepage": "https://github.com/authloop-ai/authloop/tree/main/packages/core#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/authloop-ai/authloop.git",
12
+ "directory": "packages/core"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/authloop-ai/authloop/issues"
16
+ },
17
+ "keywords": [
18
+ "authloop",
19
+ "authentication",
20
+ "cdp",
21
+ "screencast",
22
+ "e2ee",
23
+ "websocket",
24
+ "human-in-the-loop"
25
+ ],
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "types": "./dist/index.d.ts"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "debug": "^4.4.3",
44
+ "@authloop-ai/sdk": "0.2.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/debug": "^4.1.12",
48
+ "@types/node": "^22.0.0",
49
+ "typescript": "5.9.3"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc",
53
+ "check-types": "tsc --noEmit",
54
+ "test": "vitest run"
55
+ }
56
+ }