portholejs 0.1.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.
Files changed (116) hide show
  1. package/client-dist/assets/index-C_qSS_Gy.js +49 -0
  2. package/client-dist/index.html +12 -0
  3. package/dist/adb-actions.d.ts +4 -0
  4. package/dist/adb-actions.d.ts.map +1 -0
  5. package/dist/adb-actions.js +40 -0
  6. package/dist/adb-actions.js.map +1 -0
  7. package/dist/cli-errors.d.ts +5 -0
  8. package/dist/cli-errors.d.ts.map +1 -0
  9. package/dist/cli-errors.js +19 -0
  10. package/dist/cli-errors.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +542 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/control-client.d.ts +22 -0
  16. package/dist/control-client.d.ts.map +1 -0
  17. package/dist/control-client.js +100 -0
  18. package/dist/control-client.js.map +1 -0
  19. package/dist/crashes.d.ts +8 -0
  20. package/dist/crashes.d.ts.map +1 -0
  21. package/dist/crashes.js +38 -0
  22. package/dist/crashes.js.map +1 -0
  23. package/dist/device-manager.d.ts +29 -0
  24. package/dist/device-manager.d.ts.map +1 -0
  25. package/dist/device-manager.js +213 -0
  26. package/dist/device-manager.js.map +1 -0
  27. package/dist/doctor.d.ts +8 -0
  28. package/dist/doctor.d.ts.map +1 -0
  29. package/dist/doctor.js +115 -0
  30. package/dist/doctor.js.map +1 -0
  31. package/dist/engine/scrcpy-engine.d.ts +38 -0
  32. package/dist/engine/scrcpy-engine.d.ts.map +1 -0
  33. package/dist/engine/scrcpy-engine.js +216 -0
  34. package/dist/engine/scrcpy-engine.js.map +1 -0
  35. package/dist/engine/types.d.ts +26 -0
  36. package/dist/engine/types.d.ts.map +1 -0
  37. package/dist/engine/types.js +2 -0
  38. package/dist/engine/types.js.map +1 -0
  39. package/dist/index.d.ts +14 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +9 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/input-validation.d.ts +5 -0
  44. package/dist/input-validation.d.ts.map +1 -0
  45. package/dist/input-validation.js +62 -0
  46. package/dist/input-validation.js.map +1 -0
  47. package/dist/input.d.ts +22 -0
  48. package/dist/input.d.ts.map +1 -0
  49. package/dist/input.js +2 -0
  50. package/dist/input.js.map +1 -0
  51. package/dist/keycodes.d.ts +19 -0
  52. package/dist/keycodes.d.ts.map +1 -0
  53. package/dist/keycodes.js +32 -0
  54. package/dist/keycodes.js.map +1 -0
  55. package/dist/log.d.ts +3 -0
  56. package/dist/log.d.ts.map +1 -0
  57. package/dist/log.js +10 -0
  58. package/dist/log.js.map +1 -0
  59. package/dist/mcp/server.d.ts +2 -0
  60. package/dist/mcp/server.d.ts.map +1 -0
  61. package/dist/mcp/server.js +401 -0
  62. package/dist/mcp/server.js.map +1 -0
  63. package/dist/middleware.d.ts +13 -0
  64. package/dist/middleware.d.ts.map +1 -0
  65. package/dist/middleware.js +31 -0
  66. package/dist/middleware.js.map +1 -0
  67. package/dist/paths.d.ts +5 -0
  68. package/dist/paths.d.ts.map +1 -0
  69. package/dist/paths.js +27 -0
  70. package/dist/paths.js.map +1 -0
  71. package/dist/port-check.d.ts +5 -0
  72. package/dist/port-check.d.ts.map +1 -0
  73. package/dist/port-check.js +27 -0
  74. package/dist/port-check.js.map +1 -0
  75. package/dist/profiles.d.ts +4 -0
  76. package/dist/profiles.d.ts.map +1 -0
  77. package/dist/profiles.js +25 -0
  78. package/dist/profiles.js.map +1 -0
  79. package/dist/protocol.d.ts +10 -0
  80. package/dist/protocol.d.ts.map +1 -0
  81. package/dist/protocol.js +27 -0
  82. package/dist/protocol.js.map +1 -0
  83. package/dist/server/frame-convert.d.ts +6 -0
  84. package/dist/server/frame-convert.d.ts.map +1 -0
  85. package/dist/server/frame-convert.js +33 -0
  86. package/dist/server/frame-convert.js.map +1 -0
  87. package/dist/server/http.d.ts +20 -0
  88. package/dist/server/http.d.ts.map +1 -0
  89. package/dist/server/http.js +509 -0
  90. package/dist/server/http.js.map +1 -0
  91. package/dist/server/mjpeg.d.ts +21 -0
  92. package/dist/server/mjpeg.d.ts.map +1 -0
  93. package/dist/server/mjpeg.js +92 -0
  94. package/dist/server/mjpeg.js.map +1 -0
  95. package/dist/server/ws.d.ts +15 -0
  96. package/dist/server/ws.d.ts.map +1 -0
  97. package/dist/server/ws.js +102 -0
  98. package/dist/server/ws.js.map +1 -0
  99. package/dist/session.d.ts +45 -0
  100. package/dist/session.d.ts.map +1 -0
  101. package/dist/session.js +188 -0
  102. package/dist/session.js.map +1 -0
  103. package/dist/state.d.ts +27 -0
  104. package/dist/state.d.ts.map +1 -0
  105. package/dist/state.js +70 -0
  106. package/dist/state.js.map +1 -0
  107. package/dist/target-resolution.d.ts +14 -0
  108. package/dist/target-resolution.d.ts.map +1 -0
  109. package/dist/target-resolution.js +37 -0
  110. package/dist/target-resolution.js.map +1 -0
  111. package/dist/ui-tree.d.ts +47 -0
  112. package/dist/ui-tree.d.ts.map +1 -0
  113. package/dist/ui-tree.js +179 -0
  114. package/dist/ui-tree.js.map +1 -0
  115. package/package.json +63 -0
  116. package/scripts/download-scrcpy-server.mjs +67 -0
@@ -0,0 +1,38 @@
1
+ import type { InputEvent } from "../input.js";
2
+ import type { Engine, EngineMetadata, VideoChunk } from "./types.js";
3
+ export interface ScrcpyEngineOptions {
4
+ serial: string;
5
+ maxSize?: number;
6
+ maxFps?: number;
7
+ bitrate?: number;
8
+ serverPath?: string;
9
+ }
10
+ export declare class ScrcpyEngine implements Engine {
11
+ private readonly serial;
12
+ private readonly maxSize;
13
+ private readonly maxFps;
14
+ private readonly bitrate?;
15
+ private readonly serverPath;
16
+ private videoCallbacks;
17
+ private closeCallbacks;
18
+ private client;
19
+ private controller;
20
+ private adb;
21
+ private _metadata;
22
+ private lastFrame;
23
+ get metadata(): EngineMetadata | null;
24
+ constructor(opts: ScrcpyEngineOptions);
25
+ start(): Promise<void>;
26
+ private consumeVideoStream;
27
+ onVideoChunk(cb: (chunk: VideoChunk) => void): void;
28
+ onClose(cb: (error?: Error) => void): void;
29
+ sendInput(event: InputEvent): Promise<void>;
30
+ screenshot(): Promise<Uint8Array>;
31
+ captureFrame(): Promise<{
32
+ data: Uint8Array;
33
+ mime: string;
34
+ }>;
35
+ stop(): Promise<void>;
36
+ private emitClose;
37
+ }
38
+ //# sourceMappingURL=scrcpy-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrcpy-engine.d.ts","sourceRoot":"","sources":["../../src/engine/scrcpy-engine.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,YAAa,YAAW,MAAM;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,MAAM,CAA2D;IACzE,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,GAAG,CAAoB;IAC/B,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,SAAS,CAA2B;IAE5C,IAAI,QAAQ,IAAI,cAAc,GAAG,IAAI,CAEpC;gBAEW,IAAI,EAAE,mBAAmB;IAS/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0D5B,OAAO,CAAC,kBAAkB;IAiC1B,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI;IAInD,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAIpC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D3C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IASjC,YAAY,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAI3D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,OAAO,CAAC,SAAS;CAKlB"}
@@ -0,0 +1,216 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { AdbServerClient } from "@yume-chan/adb";
6
+ import { AdbScrcpyClient, AdbScrcpyOptions3_1 } from "@yume-chan/adb-scrcpy";
7
+ import { AdbServerNodeTcpConnector } from "@yume-chan/adb-server-node-tcp";
8
+ import { AndroidKeyEventAction, AndroidMotionEventAction } from "@yume-chan/scrcpy";
9
+ import { ReadableStream } from "@yume-chan/stream-extra";
10
+ import { REMOTE_BUTTON_TO_KEYCODE } from "../keycodes.js";
11
+ import { debugLog } from "../log.js";
12
+ const execFileAsync = promisify(execFile);
13
+ export class ScrcpyEngine {
14
+ serial;
15
+ maxSize;
16
+ maxFps;
17
+ bitrate;
18
+ serverPath;
19
+ videoCallbacks = [];
20
+ closeCallbacks = [];
21
+ client = null;
22
+ controller = null;
23
+ adb = null;
24
+ _metadata = null;
25
+ lastFrame = null;
26
+ get metadata() {
27
+ return this._metadata;
28
+ }
29
+ constructor(opts) {
30
+ this.serial = opts.serial;
31
+ this.maxSize = opts.maxSize ?? 1280;
32
+ this.maxFps = opts.maxFps ?? 30;
33
+ this.bitrate = opts.bitrate;
34
+ this.serverPath =
35
+ opts.serverPath ?? join(import.meta.dirname, "../../assets/scrcpy-server");
36
+ }
37
+ async start() {
38
+ debugLog("engine", `starting scrcpy for ${this.serial}`);
39
+ const connector = new AdbServerNodeTcpConnector({ port: 5037 });
40
+ const serverClient = new AdbServerClient(connector);
41
+ this.adb = await serverClient.createAdb({ serial: this.serial });
42
+ const serverBuf = await readFile(this.serverPath);
43
+ const serverStream = new ReadableStream({
44
+ start(controller) {
45
+ controller.enqueue(new Uint8Array(serverBuf));
46
+ controller.close();
47
+ },
48
+ });
49
+ await AdbScrcpyClient.pushServer(this.adb, serverStream);
50
+ const options = new AdbScrcpyOptions3_1({
51
+ video: true,
52
+ audio: false,
53
+ control: true,
54
+ maxSize: this.maxSize,
55
+ maxFps: this.maxFps,
56
+ videoBitRate: this.bitrate,
57
+ videoCodec: "h264",
58
+ tunnelForward: false,
59
+ sendFrameMeta: true,
60
+ sendCodecMeta: true,
61
+ sendDeviceMeta: true,
62
+ sendDummyByte: true,
63
+ });
64
+ this.client = await AdbScrcpyClient.start(this.adb, "/data/local/tmp/scrcpy-server.jar", options);
65
+ this.controller = this.client.controller ?? null;
66
+ const videoStream = await this.client.videoStream;
67
+ if (!videoStream) {
68
+ throw new Error("Video stream not available");
69
+ }
70
+ this._metadata = {
71
+ codec: "h264",
72
+ width: videoStream.width,
73
+ height: videoStream.height,
74
+ };
75
+ videoStream.sizeChanged(({ width, height }) => {
76
+ if (this._metadata) {
77
+ this._metadata = { ...this._metadata, width, height };
78
+ }
79
+ });
80
+ this.consumeVideoStream(videoStream.stream);
81
+ }
82
+ consumeVideoStream(stream) {
83
+ const reader = stream.getReader();
84
+ const read = async () => {
85
+ try {
86
+ while (true) {
87
+ const { done, value } = await reader.read();
88
+ if (done)
89
+ break;
90
+ const chunk = {
91
+ type: value.type === "configuration" ? "config" : "frame",
92
+ data: value.data,
93
+ timestamp: value.type === "data" && value.pts != null ? Number(value.pts) : undefined,
94
+ keyframe: value.type === "data" ? hasIdrNal(value.data) : undefined,
95
+ };
96
+ if (chunk.type === "frame") {
97
+ this.lastFrame = chunk.data;
98
+ }
99
+ for (const cb of this.videoCallbacks) {
100
+ cb(chunk);
101
+ }
102
+ }
103
+ }
104
+ catch {
105
+ this.emitClose(new Error("scrcpy video stream closed"));
106
+ }
107
+ finally {
108
+ this.emitClose();
109
+ }
110
+ };
111
+ void read();
112
+ }
113
+ onVideoChunk(cb) {
114
+ this.videoCallbacks.push(cb);
115
+ }
116
+ onClose(cb) {
117
+ this.closeCallbacks.push(cb);
118
+ }
119
+ async sendInput(event) {
120
+ if (!this.controller || !this._metadata)
121
+ return;
122
+ switch (event.kind) {
123
+ case "touch": {
124
+ const action = event.phase === "down"
125
+ ? AndroidMotionEventAction.Down
126
+ : event.phase === "up"
127
+ ? AndroidMotionEventAction.Up
128
+ : AndroidMotionEventAction.Move;
129
+ await this.controller.injectTouch({
130
+ action,
131
+ pointerId: -1n,
132
+ pointerX: Math.round(event.x * this._metadata.width),
133
+ pointerY: Math.round(event.y * this._metadata.height),
134
+ videoWidth: this._metadata.width,
135
+ videoHeight: this._metadata.height,
136
+ pressure: event.phase === "up" ? 0 : 1,
137
+ actionButton: 0,
138
+ buttons: 0,
139
+ });
140
+ break;
141
+ }
142
+ case "key": {
143
+ const action = event.phase === "down" ? AndroidKeyEventAction.Down : AndroidKeyEventAction.Up;
144
+ await this.controller.injectKeyCode({
145
+ action,
146
+ keyCode: event.keycode,
147
+ repeat: 0,
148
+ metaState: 0,
149
+ });
150
+ break;
151
+ }
152
+ case "text": {
153
+ await this.controller.injectText(event.text);
154
+ break;
155
+ }
156
+ case "remote": {
157
+ const keycode = REMOTE_BUTTON_TO_KEYCODE[event.button];
158
+ await this.controller.injectKeyCode({
159
+ action: AndroidKeyEventAction.Down,
160
+ keyCode: keycode,
161
+ repeat: 0,
162
+ metaState: 0,
163
+ });
164
+ await this.controller.injectKeyCode({
165
+ action: AndroidKeyEventAction.Up,
166
+ keyCode: keycode,
167
+ repeat: 0,
168
+ metaState: 0,
169
+ });
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ async screenshot() {
175
+ const { stdout } = await execFileAsync("adb", ["-s", this.serial, "exec-out", "screencap", "-p"], { encoding: "buffer", maxBuffer: 20 * 1024 * 1024 });
176
+ return new Uint8Array(stdout);
177
+ }
178
+ async captureFrame() {
179
+ return { data: await this.screenshot(), mime: "image/png" };
180
+ }
181
+ async stop() {
182
+ debugLog("engine", `stopping scrcpy for ${this.serial}`);
183
+ if (this.client) {
184
+ await this.client.close();
185
+ this.client = null;
186
+ }
187
+ this.controller = null;
188
+ this.adb = null;
189
+ this._metadata = null;
190
+ this.lastFrame = null;
191
+ this.videoCallbacks = [];
192
+ this.closeCallbacks = [];
193
+ }
194
+ emitClose(error) {
195
+ const callbacks = [...this.closeCallbacks];
196
+ this.closeCallbacks = [];
197
+ for (const cb of callbacks)
198
+ cb(error);
199
+ }
200
+ }
201
+ function hasIdrNal(data) {
202
+ for (let i = 0; i < data.length - 4; i++) {
203
+ if (data[i] === 0 && data[i + 1] === 0) {
204
+ let offset = i + 2;
205
+ if (data[offset] === 0)
206
+ offset++;
207
+ if (data[offset] === 1) {
208
+ const nalByte = data[offset + 1];
209
+ if (nalByte !== undefined && (nalByte & 0x1f) === 5)
210
+ return true;
211
+ }
212
+ }
213
+ }
214
+ return false;
215
+ }
216
+ //# sourceMappingURL=scrcpy-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrcpy-engine.js","sourceRoot":"","sources":["../../src/engine/scrcpy-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { InputEvent } from "../input.js";
2
+ export interface VideoChunk {
3
+ type: "config" | "frame";
4
+ data: Uint8Array;
5
+ timestamp?: number;
6
+ keyframe?: boolean;
7
+ }
8
+ export interface EngineMetadata {
9
+ codec: "h264";
10
+ width: number;
11
+ height: number;
12
+ }
13
+ export interface Engine {
14
+ start(): Promise<void>;
15
+ stop(): Promise<void>;
16
+ onVideoChunk(cb: (chunk: VideoChunk) => void): void;
17
+ onClose?(cb: (error?: Error) => void): void;
18
+ sendInput(event: InputEvent): Promise<void>;
19
+ screenshot(): Promise<Uint8Array>;
20
+ captureFrame?(): Promise<{
21
+ data: Uint8Array;
22
+ mime: string;
23
+ }>;
24
+ readonly metadata: EngineMetadata | null;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAClC,YAAY,CAAC,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;CAC1C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ export declare const VERSION = "0.0.1";
2
+ export { listDevices, findAndroidSdk, bootDevice, shutdownDevice, } from "./device-manager.js";
3
+ export { detectProfileFromConfig, detectProfile } from "./profiles.js";
4
+ export { ScrcpyEngine } from "./engine/scrcpy-engine.js";
5
+ export { Session } from "./session.js";
6
+ export { startMcpServer } from "./mcp/server.js";
7
+ export { createPortholeMiddleware, attachPortholeWs } from "./middleware.js";
8
+ export type { DeviceInfo } from "./device-manager.js";
9
+ export type { DeviceProfile } from "./profiles.js";
10
+ export type { InputEvent, TouchEvent, KeyEvent, TextEvent, RemoteEvent, } from "./input.js";
11
+ export type { Engine, EngineMetadata, VideoChunk } from "./engine/types.js";
12
+ export { AndroidKeycode, REMOTE_BUTTON_TO_KEYCODE } from "./keycodes.js";
13
+ export type { RemoteButton } from "./keycodes.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EACL,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export const VERSION = "0.0.1";
2
+ export { listDevices, findAndroidSdk, bootDevice, shutdownDevice, } from "./device-manager.js";
3
+ export { detectProfileFromConfig, detectProfile } from "./profiles.js";
4
+ export { ScrcpyEngine } from "./engine/scrcpy-engine.js";
5
+ export { Session } from "./session.js";
6
+ export { startMcpServer } from "./mcp/server.js";
7
+ export { createPortholeMiddleware, attachPortholeWs } from "./middleware.js";
8
+ export { AndroidKeycode, REMOTE_BUTTON_TO_KEYCODE } from "./keycodes.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EACL,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAW7E,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { InputEvent } from "./input.js";
2
+ import type { DeviceProfile } from "./profiles.js";
3
+ export declare function parseInputEvent(value: unknown): InputEvent;
4
+ export declare function assertInputAllowed(profile: DeviceProfile, event: InputEvent): void;
5
+ //# sourceMappingURL=input-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-validation.d.ts","sourceRoot":"","sources":["../src/input-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKnD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAqD1D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAIlF"}
@@ -0,0 +1,62 @@
1
+ import { REMOTE_BUTTON_TO_KEYCODE } from "./keycodes.js";
2
+ const TOUCH_PHASES = new Set(["down", "move", "up"]);
3
+ const KEY_PHASES = new Set(["down", "up"]);
4
+ export function parseInputEvent(value) {
5
+ if (!value || typeof value !== "object") {
6
+ throw new Error("Input event must be an object.");
7
+ }
8
+ const event = value;
9
+ if (event.kind === "touch") {
10
+ if (!TOUCH_PHASES.has(String(event.phase))) {
11
+ throw new Error("Touch phase must be down, move, or up.");
12
+ }
13
+ if (!isNormalized(event.x) || !isNormalized(event.y)) {
14
+ throw new Error("Touch coordinates must be numbers in 0..1.");
15
+ }
16
+ return {
17
+ kind: "touch",
18
+ phase: event.phase,
19
+ x: event.x,
20
+ y: event.y,
21
+ };
22
+ }
23
+ if (event.kind === "key") {
24
+ if (!KEY_PHASES.has(String(event.phase))) {
25
+ throw new Error("Key phase must be down or up.");
26
+ }
27
+ if (!Number.isInteger(event.keycode)) {
28
+ throw new Error("Keycode must be an integer.");
29
+ }
30
+ const keycode = event.keycode;
31
+ return {
32
+ kind: "key",
33
+ phase: event.phase,
34
+ keycode,
35
+ };
36
+ }
37
+ if (event.kind === "text") {
38
+ if (typeof event.text !== "string") {
39
+ throw new Error("Text input must include a string text field.");
40
+ }
41
+ return { kind: "text", text: event.text };
42
+ }
43
+ if (event.kind === "remote") {
44
+ if (typeof event.button !== "string" || !isRemoteButton(event.button)) {
45
+ throw new Error(`Remote button must be one of ${Object.keys(REMOTE_BUTTON_TO_KEYCODE).join(", ")}.`);
46
+ }
47
+ return { kind: "remote", button: event.button };
48
+ }
49
+ throw new Error("Unknown input event kind.");
50
+ }
51
+ export function assertInputAllowed(profile, event) {
52
+ if (profile === "tv" && event.kind === "touch") {
53
+ throw new Error("Touch input is not available for TV profile sessions.");
54
+ }
55
+ }
56
+ function isNormalized(value) {
57
+ return typeof value === "number" && value >= 0 && value <= 1;
58
+ }
59
+ function isRemoteButton(value) {
60
+ return Object.prototype.hasOwnProperty.call(REMOTE_BUTTON_TO_KEYCODE, value);
61
+ }
62
+ //# sourceMappingURL=input-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-validation.js","sourceRoot":"","sources":["../src/input-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { RemoteButton } from "./keycodes.js";
2
+ export interface TouchEvent {
3
+ kind: "touch";
4
+ phase: "down" | "move" | "up";
5
+ x: number;
6
+ y: number;
7
+ }
8
+ export interface KeyEvent {
9
+ kind: "key";
10
+ phase: "down" | "up";
11
+ keycode: number;
12
+ }
13
+ export interface TextEvent {
14
+ kind: "text";
15
+ text: string;
16
+ }
17
+ export interface RemoteEvent {
18
+ kind: "remote";
19
+ button: RemoteButton;
20
+ }
21
+ export type InputEvent = TouchEvent | KeyEvent | TextEvent | RemoteEvent;
22
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC"}
package/dist/input.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ export declare const AndroidKeycode: {
2
+ readonly KEYCODE_DPAD_UP: 19;
3
+ readonly KEYCODE_DPAD_DOWN: 20;
4
+ readonly KEYCODE_DPAD_LEFT: 21;
5
+ readonly KEYCODE_DPAD_RIGHT: 22;
6
+ readonly KEYCODE_DPAD_CENTER: 23;
7
+ readonly KEYCODE_BACK: 4;
8
+ readonly KEYCODE_HOME: 3;
9
+ readonly KEYCODE_MENU: 82;
10
+ readonly KEYCODE_MEDIA_PLAY_PAUSE: 85;
11
+ readonly KEYCODE_MEDIA_REWIND: 89;
12
+ readonly KEYCODE_MEDIA_FAST_FORWARD: 90;
13
+ readonly KEYCODE_VOLUME_UP: 24;
14
+ readonly KEYCODE_VOLUME_DOWN: 25;
15
+ readonly KEYCODE_WAKEUP: 224;
16
+ };
17
+ export type RemoteButton = "dpad_up" | "dpad_down" | "dpad_left" | "dpad_right" | "select" | "back" | "home" | "menu" | "play_pause" | "rewind" | "fast_forward" | "volume_up" | "volume_down";
18
+ export declare const REMOTE_BUTTON_TO_KEYCODE: Record<RemoteButton, number>;
19
+ //# sourceMappingURL=keycodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycodes.d.ts","sourceRoot":"","sources":["../src/keycodes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;CAejB,CAAC;AAEX,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,WAAW,GACX,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,MAAM,GACN,MAAM,GACN,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,cAAc,GACd,WAAW,GACX,aAAa,CAAC;AAElB,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAcjE,CAAC"}
@@ -0,0 +1,32 @@
1
+ export const AndroidKeycode = {
2
+ KEYCODE_DPAD_UP: 19,
3
+ KEYCODE_DPAD_DOWN: 20,
4
+ KEYCODE_DPAD_LEFT: 21,
5
+ KEYCODE_DPAD_RIGHT: 22,
6
+ KEYCODE_DPAD_CENTER: 23,
7
+ KEYCODE_BACK: 4,
8
+ KEYCODE_HOME: 3,
9
+ KEYCODE_MENU: 82,
10
+ KEYCODE_MEDIA_PLAY_PAUSE: 85,
11
+ KEYCODE_MEDIA_REWIND: 89,
12
+ KEYCODE_MEDIA_FAST_FORWARD: 90,
13
+ KEYCODE_VOLUME_UP: 24,
14
+ KEYCODE_VOLUME_DOWN: 25,
15
+ KEYCODE_WAKEUP: 224,
16
+ };
17
+ export const REMOTE_BUTTON_TO_KEYCODE = {
18
+ dpad_up: AndroidKeycode.KEYCODE_DPAD_UP,
19
+ dpad_down: AndroidKeycode.KEYCODE_DPAD_DOWN,
20
+ dpad_left: AndroidKeycode.KEYCODE_DPAD_LEFT,
21
+ dpad_right: AndroidKeycode.KEYCODE_DPAD_RIGHT,
22
+ select: AndroidKeycode.KEYCODE_DPAD_CENTER,
23
+ back: AndroidKeycode.KEYCODE_BACK,
24
+ home: AndroidKeycode.KEYCODE_HOME,
25
+ menu: AndroidKeycode.KEYCODE_MENU,
26
+ play_pause: AndroidKeycode.KEYCODE_MEDIA_PLAY_PAUSE,
27
+ rewind: AndroidKeycode.KEYCODE_MEDIA_REWIND,
28
+ fast_forward: AndroidKeycode.KEYCODE_MEDIA_FAST_FORWARD,
29
+ volume_up: AndroidKeycode.KEYCODE_VOLUME_UP,
30
+ volume_down: AndroidKeycode.KEYCODE_VOLUME_DOWN,
31
+ };
32
+ //# sourceMappingURL=keycodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycodes.js","sourceRoot":"","sources":["../src/keycodes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,eAAe,EAAE,EAAE;IACnB,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE,EAAE;IACrB,kBAAkB,EAAE,EAAE;IACtB,mBAAmB,EAAE,EAAE;IACvB,YAAY,EAAE,CAAC;IACf,YAAY,EAAE,CAAC;IACf,YAAY,EAAE,EAAE;IAChB,wBAAwB,EAAE,EAAE;IAC5B,oBAAoB,EAAE,EAAE;IACxB,0BAA0B,EAAE,EAAE;IAC9B,iBAAiB,EAAE,EAAE;IACrB,mBAAmB,EAAE,EAAE;IACvB,cAAc,EAAE,GAAG;CACX,CAAC;AAiBX,MAAM,CAAC,MAAM,wBAAwB,GAAiC;IACpE,OAAO,EAAE,cAAc,CAAC,eAAe;IACvC,SAAS,EAAE,cAAc,CAAC,iBAAiB;IAC3C,SAAS,EAAE,cAAc,CAAC,iBAAiB;IAC3C,UAAU,EAAE,cAAc,CAAC,kBAAkB;IAC7C,MAAM,EAAE,cAAc,CAAC,mBAAmB;IAC1C,IAAI,EAAE,cAAc,CAAC,YAAY;IACjC,IAAI,EAAE,cAAc,CAAC,YAAY;IACjC,IAAI,EAAE,cAAc,CAAC,YAAY;IACjC,UAAU,EAAE,cAAc,CAAC,wBAAwB;IACnD,MAAM,EAAE,cAAc,CAAC,oBAAoB;IAC3C,YAAY,EAAE,cAAc,CAAC,0BAA0B;IACvD,SAAS,EAAE,cAAc,CAAC,iBAAiB;IAC3C,WAAW,EAAE,cAAc,CAAC,mBAAmB;CAChD,CAAC"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type LogNamespace = "engine" | "ws" | "http" | "device" | "mcp";
2
+ export declare function debugLog(namespace: LogNamespace, message: string): void;
3
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEvE,wBAAgB,QAAQ,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMvE"}
package/dist/log.js ADDED
@@ -0,0 +1,10 @@
1
+ export function debugLog(namespace, message) {
2
+ const enabled = process.env["PORTHOLE_DEBUG"];
3
+ if (!enabled)
4
+ return;
5
+ const namespaces = new Set(enabled.split(",").map((value) => value.trim()));
6
+ if (!namespaces.has("*") && !namespaces.has(namespace))
7
+ return;
8
+ process.stderr.write(`[porthole:${namespace}] ${message}\n`);
9
+ }
10
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,SAAuB,EAAE,OAAe;IAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO;IAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,SAAS,KAAK,OAAO,IAAI,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function startMcpServer(): Promise<void>;
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAgBA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA6dpD"}