@yume-chan/adb 0.0.10 → 0.0.11

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 (318) hide show
  1. package/CHANGELOG.json +26 -0
  2. package/CHANGELOG.md +15 -1
  3. package/LICENSE +1 -1
  4. package/README.md +56 -43
  5. package/esm/adb.d.ts +48 -0
  6. package/esm/adb.d.ts.map +1 -0
  7. package/esm/adb.js +141 -146
  8. package/esm/adb.js.map +1 -1
  9. package/{dts → esm}/auth.d.ts +8 -8
  10. package/esm/auth.d.ts.map +1 -0
  11. package/esm/auth.js +68 -108
  12. package/esm/auth.js.map +1 -1
  13. package/esm/backend.d.ts +9 -0
  14. package/esm/backend.d.ts.map +1 -0
  15. package/{dts → esm}/commands/base.d.ts +1 -1
  16. package/esm/commands/base.d.ts.map +1 -0
  17. package/esm/commands/base.js +1 -0
  18. package/esm/commands/base.js.map +1 -1
  19. package/{dts → esm}/commands/framebuffer.d.ts +3 -3
  20. package/esm/commands/framebuffer.d.ts.map +1 -0
  21. package/esm/commands/framebuffer.js +15 -18
  22. package/esm/commands/framebuffer.js.map +1 -1
  23. package/esm/commands/index.d.ts +9 -0
  24. package/esm/commands/index.d.ts.map +1 -0
  25. package/esm/commands/index.js +8 -8
  26. package/esm/commands/index.js.map +1 -1
  27. package/esm/commands/install.d.ts +4 -0
  28. package/esm/commands/install.d.ts.map +1 -0
  29. package/esm/commands/install.js +21 -13
  30. package/esm/commands/install.js.map +1 -1
  31. package/esm/commands/power.d.ts +23 -0
  32. package/esm/commands/power.d.ts.map +1 -0
  33. package/esm/commands/power.js +45 -0
  34. package/esm/commands/power.js.map +1 -0
  35. package/{dts → esm}/commands/reverse.d.ts +3 -3
  36. package/esm/commands/reverse.d.ts.map +1 -0
  37. package/esm/commands/reverse.js +49 -60
  38. package/esm/commands/reverse.js.map +1 -1
  39. package/esm/commands/subprocess/index.d.ts +57 -0
  40. package/esm/commands/subprocess/index.d.ts.map +1 -0
  41. package/esm/commands/subprocess/index.js +83 -0
  42. package/esm/commands/subprocess/index.js.map +1 -0
  43. package/esm/commands/subprocess/legacy.d.ts +28 -0
  44. package/esm/commands/subprocess/legacy.d.ts.map +1 -0
  45. package/esm/commands/subprocess/legacy.js +44 -0
  46. package/esm/commands/subprocess/legacy.js.map +1 -0
  47. package/esm/commands/subprocess/protocol.d.ts +38 -0
  48. package/esm/commands/subprocess/protocol.d.ts.map +1 -0
  49. package/esm/commands/subprocess/protocol.js +155 -0
  50. package/esm/commands/subprocess/protocol.js.map +1 -0
  51. package/esm/commands/subprocess/types.d.ts +47 -0
  52. package/esm/commands/subprocess/types.d.ts.map +1 -0
  53. package/esm/commands/{shell → subprocess}/types.js +0 -0
  54. package/esm/commands/{shell → subprocess}/types.js.map +1 -1
  55. package/{dts/commands/shell → esm/commands/subprocess}/utils.d.ts +0 -0
  56. package/esm/commands/subprocess/utils.d.ts.map +1 -0
  57. package/esm/commands/{shell → subprocess}/utils.js +0 -0
  58. package/esm/commands/subprocess/utils.js.map +1 -0
  59. package/esm/commands/sync/index.d.ts +8 -0
  60. package/esm/commands/sync/index.d.ts.map +1 -0
  61. package/esm/commands/sync/index.js +7 -7
  62. package/esm/commands/sync/index.js.map +1 -1
  63. package/{dts → esm}/commands/sync/list.d.ts +4 -4
  64. package/esm/commands/sync/list.d.ts.map +1 -0
  65. package/esm/commands/sync/list.js +16 -19
  66. package/esm/commands/sync/list.js.map +1 -1
  67. package/esm/commands/sync/pull.d.ts +11 -0
  68. package/esm/commands/sync/pull.d.ts.map +1 -0
  69. package/esm/commands/sync/pull.js +17 -11
  70. package/esm/commands/sync/pull.js.map +1 -1
  71. package/esm/commands/sync/push.d.ts +8 -0
  72. package/esm/commands/sync/push.d.ts.map +1 -0
  73. package/esm/commands/sync/push.js +19 -37
  74. package/esm/commands/sync/push.js.map +1 -1
  75. package/{dts → esm}/commands/sync/request.d.ts +3 -3
  76. package/esm/commands/sync/request.d.ts.map +1 -0
  77. package/esm/commands/sync/request.js +24 -27
  78. package/esm/commands/sync/request.js.map +1 -1
  79. package/{dts → esm}/commands/sync/response.d.ts +2 -2
  80. package/esm/commands/sync/response.d.ts.map +1 -0
  81. package/esm/commands/sync/response.js +16 -20
  82. package/esm/commands/sync/response.js.map +1 -1
  83. package/{dts → esm}/commands/sync/stat.d.ts +4 -4
  84. package/esm/commands/sync/stat.d.ts.map +1 -0
  85. package/esm/commands/sync/stat.js +22 -27
  86. package/esm/commands/sync/stat.js.map +1 -1
  87. package/esm/commands/sync/sync.d.ts +83 -0
  88. package/esm/commands/sync/sync.d.ts.map +1 -0
  89. package/esm/commands/sync/sync.js +92 -89
  90. package/esm/commands/sync/sync.js.map +1 -1
  91. package/{dts → esm}/commands/tcpip.d.ts +1 -1
  92. package/{dts → esm}/commands/tcpip.d.ts.map +1 -1
  93. package/esm/commands/tcpip.js +14 -19
  94. package/esm/commands/tcpip.js.map +1 -1
  95. package/{dts → esm}/crypto.d.ts +7 -7
  96. package/esm/crypto.d.ts.map +1 -0
  97. package/esm/crypto.js +26 -29
  98. package/esm/crypto.js.map +1 -1
  99. package/{dts → esm}/features.d.ts +0 -0
  100. package/{dts → esm}/features.d.ts.map +0 -0
  101. package/esm/features.js +1 -1
  102. package/esm/features.js.map +1 -1
  103. package/esm/index.d.ts +11 -0
  104. package/esm/index.d.ts.map +1 -0
  105. package/esm/index.js +10 -10
  106. package/esm/index.js.map +1 -1
  107. package/esm/packet.d.ts +28 -0
  108. package/esm/packet.d.ts.map +1 -0
  109. package/esm/packet.js +26 -51
  110. package/esm/packet.js.map +1 -1
  111. package/esm/socket/controller.d.ts +37 -0
  112. package/esm/socket/controller.d.ts.map +1 -0
  113. package/esm/socket/controller.js +52 -66
  114. package/esm/socket/controller.js.map +1 -1
  115. package/{dts → esm}/socket/dispatcher.d.ts +10 -23
  116. package/esm/socket/dispatcher.d.ts.map +1 -0
  117. package/esm/socket/dispatcher.js +148 -164
  118. package/esm/socket/dispatcher.js.map +1 -1
  119. package/esm/socket/index.d.ts +3 -0
  120. package/esm/socket/index.d.ts.map +1 -0
  121. package/esm/socket/index.js +2 -6
  122. package/esm/socket/index.js.map +1 -1
  123. package/esm/socket/socket.d.ts +34 -0
  124. package/esm/socket/socket.d.ts.map +1 -0
  125. package/esm/socket/socket.js +69 -14
  126. package/esm/socket/socket.js.map +1 -1
  127. package/esm/stream/buffered.d.ts +35 -0
  128. package/esm/stream/buffered.d.ts.map +1 -0
  129. package/esm/stream/buffered.js +123 -0
  130. package/esm/stream/buffered.js.map +1 -0
  131. package/esm/stream/detect.bak.d.ts +240 -0
  132. package/esm/stream/detect.bak.d.ts.map +1 -0
  133. package/esm/stream/detect.bak.js +60 -0
  134. package/esm/stream/detect.bak.js.map +1 -0
  135. package/esm/stream/detect.d.ts +18 -0
  136. package/esm/stream/detect.d.ts.map +1 -0
  137. package/esm/stream/detect.js +6 -0
  138. package/esm/stream/detect.js.map +1 -0
  139. package/esm/stream/detect.native.d.ts +240 -0
  140. package/esm/stream/detect.native.d.ts.map +1 -0
  141. package/esm/stream/detect.native.js +60 -0
  142. package/esm/stream/detect.native.js.map +1 -0
  143. package/esm/stream/detect.polyfill.d.ts +18 -0
  144. package/esm/stream/detect.polyfill.d.ts.map +1 -0
  145. package/esm/stream/detect.polyfill.js +6 -0
  146. package/esm/stream/detect.polyfill.js.map +1 -0
  147. package/esm/stream/index.d.ts +4 -0
  148. package/esm/stream/index.d.ts.map +1 -0
  149. package/esm/stream/index.js +3 -2
  150. package/esm/stream/index.js.map +1 -1
  151. package/esm/stream/transform.d.ts +98 -0
  152. package/esm/stream/transform.d.ts.map +1 -0
  153. package/esm/stream/transform.js +352 -0
  154. package/esm/stream/transform.js.map +1 -0
  155. package/{dts → esm}/utils/auto-reset-event.d.ts +1 -1
  156. package/esm/utils/auto-reset-event.d.ts.map +1 -0
  157. package/esm/utils/auto-reset-event.js +2 -1
  158. package/esm/utils/auto-reset-event.js.map +1 -1
  159. package/esm/utils/base64.d.ts +5 -0
  160. package/esm/utils/base64.d.ts.map +1 -0
  161. package/esm/utils/base64.js +116 -80
  162. package/esm/utils/base64.js.map +1 -1
  163. package/esm/utils/encoding.d.ts +3 -0
  164. package/{dts → esm}/utils/encoding.d.ts.map +1 -1
  165. package/esm/utils/encoding.js +1 -1
  166. package/esm/utils/encoding.js.map +1 -1
  167. package/esm/utils/index.d.ts +4 -0
  168. package/esm/utils/index.d.ts.map +1 -0
  169. package/esm/utils/index.js +3 -5
  170. package/esm/utils/index.js.map +1 -1
  171. package/package.json +50 -44
  172. package/src/adb.ts +147 -143
  173. package/src/auth.ts +23 -19
  174. package/src/backend.ts +3 -12
  175. package/src/commands/base.ts +1 -1
  176. package/src/commands/framebuffer.ts +4 -4
  177. package/src/commands/index.ts +8 -8
  178. package/src/commands/install.ts +26 -15
  179. package/src/commands/power.ts +54 -0
  180. package/src/commands/reverse.ts +10 -8
  181. package/src/commands/subprocess/index.ts +124 -0
  182. package/src/commands/subprocess/legacy.ts +58 -0
  183. package/src/commands/subprocess/protocol.ts +185 -0
  184. package/src/commands/subprocess/types.ts +55 -0
  185. package/src/commands/{shell → subprocess}/utils.ts +0 -0
  186. package/src/commands/sync/index.ts +7 -7
  187. package/src/commands/sync/list.ts +7 -6
  188. package/src/commands/sync/pull.ts +27 -18
  189. package/src/commands/sync/push.ts +25 -30
  190. package/src/commands/sync/request.ts +7 -7
  191. package/src/commands/sync/response.ts +4 -4
  192. package/src/commands/sync/stat.ts +8 -6
  193. package/src/commands/sync/sync.ts +64 -32
  194. package/src/commands/tcpip.ts +3 -3
  195. package/src/crypto.ts +38 -43
  196. package/src/index.ts +11 -10
  197. package/src/packet.ts +37 -52
  198. package/src/socket/dispatcher.ts +90 -103
  199. package/src/socket/index.ts +2 -6
  200. package/src/socket/socket.ts +117 -16
  201. package/src/stream/buffered.ts +147 -0
  202. package/src/stream/detect.native.ts +362 -0
  203. package/src/stream/detect.polyfill.ts +25 -0
  204. package/src/stream/detect.ts +25 -0
  205. package/src/stream/index.ts +3 -2
  206. package/src/stream/transform.ts +427 -0
  207. package/src/utils/auto-reset-event.ts +1 -1
  208. package/src/utils/base64.ts +161 -129
  209. package/src/utils/encoding.ts +3 -3
  210. package/src/utils/index.ts +3 -5
  211. package/tsconfig.build.json +3 -0
  212. package/tsconfig.build.tsbuildinfo +1 -0
  213. package/tsconfig.test.json +10 -0
  214. package/dts/adb.d.ts +0 -46
  215. package/dts/adb.d.ts.map +0 -1
  216. package/dts/auth.d.ts.map +0 -1
  217. package/dts/backend.d.ts +0 -13
  218. package/dts/backend.d.ts.map +0 -1
  219. package/dts/commands/base.d.ts.map +0 -1
  220. package/dts/commands/demo-mode.d.ts +0 -41
  221. package/dts/commands/demo-mode.d.ts.map +0 -1
  222. package/dts/commands/framebuffer.d.ts.map +0 -1
  223. package/dts/commands/index.d.ts +0 -9
  224. package/dts/commands/index.d.ts.map +0 -1
  225. package/dts/commands/install.d.ts +0 -3
  226. package/dts/commands/install.d.ts.map +0 -1
  227. package/dts/commands/reverse.d.ts.map +0 -1
  228. package/dts/commands/shell/index.d.ts +0 -46
  229. package/dts/commands/shell/index.d.ts.map +0 -1
  230. package/dts/commands/shell/legacy.d.ts +0 -38
  231. package/dts/commands/shell/legacy.d.ts.map +0 -1
  232. package/dts/commands/shell/protocol.d.ts +0 -45
  233. package/dts/commands/shell/protocol.d.ts.map +0 -1
  234. package/dts/commands/shell/types.d.ts +0 -47
  235. package/dts/commands/shell/types.d.ts.map +0 -1
  236. package/dts/commands/shell/utils.d.ts.map +0 -1
  237. package/dts/commands/sync/index.d.ts +0 -8
  238. package/dts/commands/sync/index.d.ts.map +0 -1
  239. package/dts/commands/sync/list.d.ts.map +0 -1
  240. package/dts/commands/sync/pull.d.ts +0 -11
  241. package/dts/commands/sync/pull.d.ts.map +0 -1
  242. package/dts/commands/sync/push.d.ts +0 -8
  243. package/dts/commands/sync/push.d.ts.map +0 -1
  244. package/dts/commands/sync/request.d.ts.map +0 -1
  245. package/dts/commands/sync/response.d.ts.map +0 -1
  246. package/dts/commands/sync/stat.d.ts.map +0 -1
  247. package/dts/commands/sync/sync.d.ts +0 -68
  248. package/dts/commands/sync/sync.d.ts.map +0 -1
  249. package/dts/crypto.d.ts.map +0 -1
  250. package/dts/index.d.ts +0 -11
  251. package/dts/index.d.ts.map +0 -1
  252. package/dts/packet.d.ts +0 -27
  253. package/dts/packet.d.ts.map +0 -1
  254. package/dts/socket/close-event-emitter.d.ts +0 -8
  255. package/dts/socket/close-event-emitter.d.ts.map +0 -1
  256. package/dts/socket/controller.d.ts +0 -43
  257. package/dts/socket/controller.d.ts.map +0 -1
  258. package/dts/socket/data-event-emitter.d.ts +0 -10
  259. package/dts/socket/data-event-emitter.d.ts.map +0 -1
  260. package/dts/socket/dispatcher.d.ts.map +0 -1
  261. package/dts/socket/index.d.ts +0 -7
  262. package/dts/socket/index.d.ts.map +0 -1
  263. package/dts/socket/logger.d.ts +0 -9
  264. package/dts/socket/logger.d.ts.map +0 -1
  265. package/dts/socket/socket.d.ts +0 -22
  266. package/dts/socket/socket.d.ts.map +0 -1
  267. package/dts/stream/buffered-stream.d.ts +0 -28
  268. package/dts/stream/buffered-stream.d.ts.map +0 -1
  269. package/dts/stream/index.d.ts +0 -3
  270. package/dts/stream/index.d.ts.map +0 -1
  271. package/dts/stream/stream.d.ts +0 -15
  272. package/dts/stream/stream.d.ts.map +0 -1
  273. package/dts/utils/auto-reset-event.d.ts.map +0 -1
  274. package/dts/utils/base64.d.ts +0 -7
  275. package/dts/utils/base64.d.ts.map +0 -1
  276. package/dts/utils/chunk.d.ts +0 -3
  277. package/dts/utils/chunk.d.ts.map +0 -1
  278. package/dts/utils/encoding.d.ts +0 -3
  279. package/dts/utils/event-queue.d.ts +0 -35
  280. package/dts/utils/event-queue.d.ts.map +0 -1
  281. package/dts/utils/index.d.ts +0 -6
  282. package/dts/utils/index.d.ts.map +0 -1
  283. package/esm/commands/demo-mode.js +0 -170
  284. package/esm/commands/demo-mode.js.map +0 -1
  285. package/esm/commands/shell/index.js +0 -62
  286. package/esm/commands/shell/index.js.map +0 -1
  287. package/esm/commands/shell/legacy.js +0 -49
  288. package/esm/commands/shell/legacy.js.map +0 -1
  289. package/esm/commands/shell/protocol.js +0 -106
  290. package/esm/commands/shell/protocol.js.map +0 -1
  291. package/esm/commands/shell/utils.js.map +0 -1
  292. package/esm/socket/close-event-emitter.js +0 -28
  293. package/esm/socket/close-event-emitter.js.map +0 -1
  294. package/esm/socket/data-event-emitter.js +0 -25
  295. package/esm/socket/data-event-emitter.js.map +0 -1
  296. package/esm/socket/logger.js +0 -2
  297. package/esm/socket/logger.js.map +0 -1
  298. package/esm/stream/buffered-stream.js +0 -67
  299. package/esm/stream/buffered-stream.js.map +0 -1
  300. package/esm/stream/stream.js +0 -41
  301. package/esm/stream/stream.js.map +0 -1
  302. package/esm/utils/chunk.js +0 -70
  303. package/esm/utils/chunk.js.map +0 -1
  304. package/esm/utils/event-queue.js +0 -104
  305. package/esm/utils/event-queue.js.map +0 -1
  306. package/src/commands/demo-mode.ts +0 -160
  307. package/src/commands/shell/index.ts +0 -87
  308. package/src/commands/shell/legacy.ts +0 -59
  309. package/src/commands/shell/protocol.ts +0 -121
  310. package/src/commands/shell/types.ts +0 -55
  311. package/src/socket/close-event-emitter.ts +0 -28
  312. package/src/socket/controller.ts +0 -105
  313. package/src/socket/data-event-emitter.ts +0 -27
  314. package/src/socket/logger.ts +0 -12
  315. package/src/stream/buffered-stream.ts +0 -92
  316. package/src/stream/stream.ts +0 -50
  317. package/src/utils/chunk.ts +0 -70
  318. package/src/utils/event-queue.ts +0 -145
@@ -1,12 +1,14 @@
1
+ // cspell: ignore killforward
2
+
1
3
  import { AutoDisposable } from '@yume-chan/event';
2
4
  import Struct from '@yume-chan/struct';
3
- import type { AdbPacket } from '../packet';
4
- import type { AdbIncomingSocketEventArgs, AdbPacketDispatcher, AdbSocket } from '../socket';
5
- import { AdbBufferedStream } from '../stream';
6
- import { decodeUtf8 } from "../utils";
5
+ import type { AdbPacketCore } from '../packet.js';
6
+ import type { AdbIncomingSocketEventArgs, AdbPacketDispatcher, AdbSocket } from '../socket/index.js';
7
+ import { AdbBufferedStream } from '../stream/index.js';
8
+ import { decodeUtf8 } from "../utils/index.js";
7
9
 
8
10
  export interface AdbReverseHandler {
9
- onSocket(packet: AdbPacket, socket: AdbSocket): void;
11
+ onSocket(packet: AdbPacketCore, socket: AdbSocket): void;
10
12
  }
11
13
 
12
14
  export interface AdbForwardListener {
@@ -50,8 +52,8 @@ export class AdbReverseCommand extends AutoDisposable {
50
52
  return;
51
53
  }
52
54
 
53
- const address = decodeUtf8(e.packet.payload!);
54
- // tcp:1234\0
55
+ const address = decodeUtf8(e.packet.payload);
56
+ // Address format: `tcp:12345\0`
55
57
  const port = Number.parseInt(address.substring(4));
56
58
  if (this.localPortToHandler.has(port)) {
57
59
  this.localPortToHandler.get(port)!.onSocket(e.packet, e.socket);
@@ -78,7 +80,7 @@ export class AdbReverseCommand extends AutoDisposable {
78
80
 
79
81
  const response = await AdbReverseStringResponse.deserialize(stream);
80
82
  return response.content!.split('\n').map(line => {
81
- const [deviceSerial, localName, remoteName] = line.split(' ');
83
+ const [deviceSerial, localName, remoteName] = line.split(' ') as [string, string, string];
82
84
  return { deviceSerial, localName, remoteName };
83
85
  });
84
86
 
@@ -0,0 +1,124 @@
1
+ import type { Adb } from '../../adb.js';
2
+ import { DecodeUtf8Stream, GatherStringStream } from "../../stream/index.js";
3
+ import { AdbNoneSubprocessProtocol } from './legacy.js';
4
+ import { AdbShellSubprocessProtocol } from './protocol.js';
5
+ import type { AdbSubprocessProtocol, AdbSubprocessProtocolConstructor } from './types.js';
6
+
7
+ export * from './legacy.js';
8
+ export * from './protocol.js';
9
+ export * from './types.js';
10
+ export * from './utils.js';
11
+
12
+ export interface AdbSubprocessOptions {
13
+ /**
14
+ * A list of `AdbShellConstructor`s to be used.
15
+ *
16
+ * Different `AdbShell` has different capabilities, thus requires specific adaptations.
17
+ * Check each `AdbShell`'s documentation for details.
18
+ *
19
+ * The first one whose `isSupported` returns `true` will be used.
20
+ * If no `AdbShell` is supported, an error will be thrown.
21
+ *
22
+ * The default value is `[AdbShellProtocol, AdbLegacyShell]`.
23
+ */
24
+ protocols: AdbSubprocessProtocolConstructor[];
25
+ }
26
+
27
+ const DefaultOptions: AdbSubprocessOptions = {
28
+ protocols: [AdbShellSubprocessProtocol, AdbNoneSubprocessProtocol],
29
+ };
30
+
31
+ export interface SubprocessResult {
32
+ stdout: string;
33
+ stderr: string;
34
+ exitCode: number;
35
+ }
36
+
37
+ export class AdbSubprocess {
38
+ public readonly adb: Adb;
39
+
40
+ public constructor(adb: Adb) {
41
+ this.adb = adb;
42
+ }
43
+
44
+ private async createProtocol(command: string, options?: Partial<AdbSubprocessOptions>): Promise<AdbSubprocessProtocol> {
45
+ let { protocols } = { ...DefaultOptions, ...options };
46
+
47
+ let Constructor: AdbSubprocessProtocolConstructor | undefined;
48
+ for (const item of protocols) {
49
+ if (await item.isSupported(this.adb)) {
50
+ Constructor = item;
51
+ break;
52
+ }
53
+ }
54
+
55
+ if (!Constructor) {
56
+ throw new Error('No specified protocol is supported by the device');
57
+ }
58
+
59
+ return await Constructor.spawn(this.adb, command);
60
+ }
61
+
62
+ /**
63
+ * Spawns the default shell in interactive mode.
64
+ * @param options The options for creating the `AdbShell`
65
+ * @returns A new `AdbShell` instance connecting to the spawned shell process.
66
+ */
67
+ public shell(options?: Partial<AdbSubprocessOptions>): Promise<AdbSubprocessProtocol> {
68
+ return this.createProtocol('', options);
69
+ }
70
+
71
+ /**
72
+ * Spawns a new process using the given `command`.
73
+ * @param command The command to run, or an array of strings containing both command and args.
74
+ * @param options The options for creating the `AdbShell`
75
+ * @returns A new `AdbShell` instance connecting to the spawned process.
76
+ */
77
+ public spawn(command: string | string[], options?: Partial<AdbSubprocessOptions>): Promise<AdbSubprocessProtocol> {
78
+ if (Array.isArray(command)) {
79
+ command = command.join(' ');
80
+ }
81
+ return this.createProtocol(command, options);
82
+ }
83
+
84
+ /**
85
+ * Spawns a new process, waits until it exits, and returns the entire output.
86
+ * @param command The command to run
87
+ * @param options The options for creating the `AdbShell`
88
+ * @returns The entire output of the command
89
+ */
90
+ public async spawnAndWait(
91
+ command: string | string[],
92
+ options?: Partial<AdbSubprocessOptions>
93
+ ): Promise<SubprocessResult> {
94
+ const shell = await this.spawn(command, options);
95
+
96
+ const stdout = new GatherStringStream();
97
+ shell.stdout
98
+ .pipeThrough(new DecodeUtf8Stream())
99
+ .pipeThrough(stdout);
100
+
101
+ const stderr = new GatherStringStream();
102
+ shell.stderr
103
+ .pipeThrough(new DecodeUtf8Stream())
104
+ .pipeThrough(stderr);
105
+
106
+ const exitCode = await shell.exit;
107
+
108
+ return {
109
+ stdout: stdout.result,
110
+ stderr: stderr.result,
111
+ exitCode,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Spawns a new process, waits until it exits, and returns the entire output.
117
+ * @param command The command to run
118
+ * @returns The entire output of the command
119
+ */
120
+ public async spawnAndWaitLegacy(command: string | string[]): Promise<string> {
121
+ const { stdout } = await this.spawnAndWait(command, { protocols: [AdbNoneSubprocessProtocol] });
122
+ return stdout;
123
+ }
124
+ }
@@ -0,0 +1,58 @@
1
+ import type { Adb } from "../../adb.js";
2
+ import type { AdbSocket } from "../../socket/index.js";
3
+ import { DuplexStreamFactory, type ReadableStream } from "../../stream/index.js";
4
+ import type { AdbSubprocessProtocol } from "./types.js";
5
+
6
+ /**
7
+ * The legacy shell
8
+ *
9
+ * Features:
10
+ * * `stderr`: No
11
+ * * `exit` exit code: No
12
+ * * `resize`: No
13
+ */
14
+ export class AdbNoneSubprocessProtocol implements AdbSubprocessProtocol {
15
+ public static isSupported() { return true; }
16
+
17
+ public static async spawn(adb: Adb, command: string) {
18
+ return new AdbNoneSubprocessProtocol(await adb.createSocket(`shell:${command}`));
19
+ }
20
+
21
+ private readonly socket: AdbSocket;
22
+
23
+ // Legacy shell forwards all data to stdin.
24
+ public get stdin() { return this.socket.writable; }
25
+
26
+ private _stdout: ReadableStream<Uint8Array>;
27
+ // Legacy shell doesn't support splitting output streams.
28
+ public get stdout() { return this._stdout; }
29
+
30
+ // `stderr` of Legacy shell is always empty.
31
+ private _stderr: ReadableStream<Uint8Array>;
32
+ public get stderr() { return this._stderr; }
33
+
34
+ private _exit: Promise<number>;
35
+ public get exit() { return this._exit; }
36
+
37
+ public constructor(socket: AdbSocket) {
38
+ this.socket = socket;
39
+
40
+ const factory = new DuplexStreamFactory<Uint8Array, Uint8Array>({
41
+ close: async () => {
42
+ await this.socket.close();
43
+ },
44
+ });
45
+
46
+ this._stdout = factory.createWrapReadable(this.socket.readable);
47
+ this._stderr = factory.createReadable();
48
+ this._exit = factory.closed.then(() => 0);
49
+ }
50
+
51
+ public resize() {
52
+ // Not supported
53
+ }
54
+
55
+ public kill() {
56
+ return this.socket.close();
57
+ }
58
+ }
@@ -0,0 +1,185 @@
1
+ import { PromiseResolver } from "@yume-chan/async";
2
+ import Struct, { placeholder, type StructValueType } from "@yume-chan/struct";
3
+ import type { Adb } from "../../adb.js";
4
+ import { AdbFeatures } from "../../features.js";
5
+ import type { AdbSocket } from "../../socket/index.js";
6
+ import { PushReadableStream, ReadableStream, StructDeserializeStream, StructSerializeStream, TransformStream, WritableStream, WritableStreamDefaultWriter, type PushReadableStreamController } from "../../stream/index.js";
7
+ import { encodeUtf8 } from "../../utils/index.js";
8
+ import type { AdbSubprocessProtocol } from "./types.js";
9
+
10
+ export enum AdbShellProtocolId {
11
+ Stdin,
12
+ Stdout,
13
+ Stderr,
14
+ Exit,
15
+ CloseStdin,
16
+ WindowSizeChange,
17
+ }
18
+
19
+ // This packet format is used in both direction.
20
+ const AdbShellProtocolPacket = new Struct({ littleEndian: true })
21
+ .uint8('id', placeholder<AdbShellProtocolId>())
22
+ .uint32('length')
23
+ .uint8Array('data', { lengthField: 'length' });
24
+
25
+ type AdbShellProtocolPacketInit = typeof AdbShellProtocolPacket['TInit'];
26
+
27
+ type AdbShellProtocolPacket = StructValueType<typeof AdbShellProtocolPacket>;
28
+
29
+ class StdinSerializeStream extends TransformStream<Uint8Array, AdbShellProtocolPacketInit>{
30
+ constructor() {
31
+ super({
32
+ transform(chunk, controller) {
33
+ controller.enqueue({
34
+ id: AdbShellProtocolId.Stdin,
35
+ data: chunk,
36
+ });
37
+ },
38
+ flush() {
39
+ // TODO: AdbShellSubprocessProtocol: support closing stdin
40
+ }
41
+ });
42
+ }
43
+ }
44
+
45
+ class StdoutDeserializeStream extends TransformStream<AdbShellProtocolPacket, Uint8Array>{
46
+ constructor(type: AdbShellProtocolId.Stdout | AdbShellProtocolId.Stderr) {
47
+ super({
48
+ transform(chunk, controller) {
49
+ if (chunk.id === type) {
50
+ controller.enqueue(chunk.data);
51
+ }
52
+ },
53
+ });
54
+ }
55
+ }
56
+
57
+ class MultiplexStream<T>{
58
+ private _readable: PushReadableStream<T>;
59
+ private _readableController!: PushReadableStreamController<T>;
60
+ public get readable() { return this._readable; }
61
+
62
+ private _activeCount = 0;
63
+
64
+ constructor() {
65
+ this._readable = new PushReadableStream(controller => {
66
+ this._readableController = controller;
67
+ });
68
+ }
69
+
70
+ public createWriteable() {
71
+ return new WritableStream<T>({
72
+ start: () => {
73
+ this._activeCount += 1;
74
+ },
75
+ write: async (chunk) => {
76
+ await this._readableController.enqueue(chunk);
77
+ },
78
+ abort: async (e) => {
79
+ this._activeCount -= 1;
80
+ if (this._activeCount === 0) {
81
+ this._readableController.close();
82
+ }
83
+ },
84
+ close: async () => {
85
+ this._activeCount -= 1;
86
+ if (this._activeCount === 0) {
87
+ this._readableController.close();
88
+ }
89
+ },
90
+ });
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Shell v2 a.k.a Shell Protocol
96
+ *
97
+ * Features:
98
+ * * `stderr`: Yes
99
+ * * `exit` exit code: Yes
100
+ * * `resize`: Yes
101
+ */
102
+ export class AdbShellSubprocessProtocol implements AdbSubprocessProtocol {
103
+ public static isSupported(adb: Adb) {
104
+ return adb.features!.includes(AdbFeatures.ShellV2);
105
+ }
106
+
107
+ public static async spawn(adb: Adb, command: string) {
108
+ // TODO: AdbShellSubprocessProtocol: Support raw mode
109
+ // TODO: AdbShellSubprocessProtocol: Support setting `XTERM` environment variable
110
+ return new AdbShellSubprocessProtocol(await adb.createSocket(`shell,v2,pty:${command}`));
111
+ }
112
+
113
+ private readonly _socket: AdbSocket;
114
+ private _socketWriter: WritableStreamDefaultWriter<AdbShellProtocolPacketInit>;
115
+
116
+ private _stdin: WritableStream<Uint8Array>;
117
+ public get stdin() { return this._stdin; }
118
+
119
+ private _stdout: ReadableStream<Uint8Array>;
120
+ public get stdout() { return this._stdout; }
121
+
122
+ private _stderr: ReadableStream<Uint8Array>;
123
+ public get stderr() { return this._stderr; }
124
+
125
+ private readonly _exit = new PromiseResolver<number>();
126
+ public get exit() { return this._exit.promise; }
127
+
128
+ public constructor(socket: AdbSocket) {
129
+ this._socket = socket;
130
+
131
+ // Check this image to help you understand the stream graph
132
+ // cspell: disable-next-line
133
+ // https://www.plantuml.com/plantuml/png/bL91QiCm4Bpx5SAdv90lb1JISmiw5XzaQKf5PIkiLZIqzEyLSg8ks13gYtOykpFhiOw93N6UGjVDqK7rZsxKqNw0U_NTgVAy4empOy2mm4_olC0VEVEE47GUpnGjKdgXoD76q4GIEpyFhOwP_m28hW0NNzxNUig1_JdW0bA7muFIJDco1daJ_1SAX9bgvoPJPyIkSekhNYctvIGXrCH6tIsPL5fs-s6J5yc9BpWXhKtNdF2LgVYPGM_6GlMwfhWUsIt4lbScANrwlgVVUifPSVi__t44qStnwPvZwobdSmHHlL57p2vFuHS0
134
+
135
+ // TODO: AdbShellSubprocessProtocol: Optimize stream graph
136
+
137
+ const [stdout, stderr] = socket.readable
138
+ .pipeThrough(new StructDeserializeStream(AdbShellProtocolPacket))
139
+ .pipeThrough(new TransformStream<AdbShellProtocolPacket, AdbShellProtocolPacket>({
140
+ transform: (chunk, controller) => {
141
+ if (chunk.id === AdbShellProtocolId.Exit) {
142
+ this._exit.resolve(new Uint8Array(chunk.data)[0]!);
143
+ // We can let `StdoutTransformStream` to process `AdbShellProtocolId.Exit`,
144
+ // but since we need this `TransformStream` to capture the exit code anyway,
145
+ // terminating child streams here is killing two birds with one stone.
146
+ controller.terminate();
147
+ return;
148
+ }
149
+ controller.enqueue(chunk);
150
+ }
151
+ }))
152
+ .tee();
153
+ this._stdout = stdout
154
+ .pipeThrough(new StdoutDeserializeStream(AdbShellProtocolId.Stdout));
155
+ this._stderr = stderr
156
+ .pipeThrough(new StdoutDeserializeStream(AdbShellProtocolId.Stderr));
157
+
158
+ const multiplexer = new MultiplexStream<AdbShellProtocolPacketInit>();
159
+ multiplexer.readable
160
+ .pipeThrough(new StructSerializeStream(AdbShellProtocolPacket))
161
+ .pipeTo(socket.writable);
162
+
163
+ const { readable, writable } = new StdinSerializeStream();
164
+ this._stdin = writable;
165
+ readable.pipeTo(multiplexer.createWriteable());
166
+
167
+ this._socketWriter = multiplexer.createWriteable().getWriter();
168
+ }
169
+
170
+ public async resize(rows: number, cols: number) {
171
+ await this._socketWriter.write({
172
+ id: AdbShellProtocolId.WindowSizeChange,
173
+ data: encodeUtf8(
174
+ // The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}`
175
+ // However, according to https://linux.die.net/man/4/tty_ioctl
176
+ // `x_pixels` and `y_pixels` are not used, so always passing `0` is fine.
177
+ `${rows}x${cols},0x0\0`
178
+ ),
179
+ });
180
+ }
181
+
182
+ public kill() {
183
+ return this._socket.close();
184
+ }
185
+ }
@@ -0,0 +1,55 @@
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
+ import type { Adb } from "../../adb.js";
3
+ import type { AdbSocket } from "../../socket/index.js";
4
+ import type { ReadableStream, WritableStream } from "../../stream/index.js";
5
+
6
+ export interface AdbSubprocessProtocol {
7
+ /**
8
+ * A WritableStream that writes to the `stdin` pipe.
9
+ */
10
+ readonly stdin: WritableStream<Uint8Array>;
11
+
12
+ /**
13
+ * The `stdout` pipe of the process.
14
+ */
15
+ readonly stdout: ReadableStream<Uint8Array>;
16
+
17
+ /**
18
+ * The `stderr` pipe of the process.
19
+ *
20
+ * Note: Some `AdbShell` doesn't separate `stdout` and `stderr`,
21
+ * All output will be sent to `stdout`.
22
+ */
23
+ readonly stderr: ReadableStream<Uint8Array>;
24
+
25
+ /**
26
+ * A `Promise` that resolves to the exit code of the process.
27
+ *
28
+ * Note: Some `AdbShell` doesn't support exit code,
29
+ * They will always resolve with `0`.
30
+ */
31
+ readonly exit: Promise<number>;
32
+
33
+ /**
34
+ * Resizes the current shell.
35
+ *
36
+ * Some `AdbShell`s may not support resizing and will always ignore calls to this method.
37
+ */
38
+ resize(rows: number, cols: number): ValueOrPromise<void>;
39
+
40
+ /**
41
+ * Kills the current process.
42
+ */
43
+ kill(): ValueOrPromise<void>;
44
+ }
45
+
46
+ export interface AdbSubprocessProtocolConstructor {
47
+ /** Returns `true` if the `adb` instance supports this shell */
48
+ isSupported(adb: Adb): ValueOrPromise<boolean>;
49
+
50
+ /** Creates a new `AdbShell` using the specified `Adb` and `command` */
51
+ spawn(adb: Adb, command: string): ValueOrPromise<AdbSubprocessProtocol>;
52
+
53
+ /** Creates a new `AdbShell` by attaching to an exist `AdbSocket` */
54
+ new(socket: AdbSocket): AdbSubprocessProtocol;
55
+ }
File without changes
@@ -1,7 +1,7 @@
1
- export * from './list';
2
- export * from './pull';
3
- export * from './request';
4
- export * from './response';
5
- export * from './push';
6
- export * from './stat';
7
- export * from './sync';
1
+ export * from './list.js';
2
+ export * from './pull.js';
3
+ export * from './request.js';
4
+ export * from './response.js';
5
+ export * from './push.js';
6
+ export * from './stat.js';
7
+ export * from './sync.js';
@@ -1,8 +1,8 @@
1
1
  import Struct from '@yume-chan/struct';
2
- import { AdbBufferedStream } from '../../stream';
3
- import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
4
- import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';
5
- import { AdbSyncLstatResponse } from './stat';
2
+ import type { AdbBufferedStream, WritableStreamDefaultWriter } from '../../stream/index.js';
3
+ import { AdbSyncRequestId, adbSyncWriteRequest } from './request.js';
4
+ import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response.js';
5
+ import { AdbSyncLstatResponse } from './stat.js';
6
6
 
7
7
  export const AdbSyncEntryResponse =
8
8
  new Struct({ littleEndian: true })
@@ -20,9 +20,10 @@ const ResponseTypes = {
20
20
 
21
21
  export async function* adbSyncOpenDir(
22
22
  stream: AdbBufferedStream,
23
- path: string
23
+ writer: WritableStreamDefaultWriter<Uint8Array>,
24
+ path: string,
24
25
  ): AsyncGenerator<AdbSyncEntryResponse, void, void> {
25
- await adbSyncWriteRequest(stream, AdbSyncRequestId.List, path);
26
+ await adbSyncWriteRequest(writer, AdbSyncRequestId.List, path);
26
27
 
27
28
  while (true) {
28
29
  const response = await adbSyncReadResponse(stream, ResponseTypes);
@@ -1,12 +1,12 @@
1
1
  import Struct from '@yume-chan/struct';
2
- import { AdbBufferedStream } from '../../stream';
3
- import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
4
- import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';
2
+ import { AdbBufferedStream, ReadableStream, WritableStreamDefaultWriter } from '../../stream/index.js';
3
+ import { AdbSyncRequestId, adbSyncWriteRequest } from './request.js';
4
+ import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response.js';
5
5
 
6
6
  export const AdbSyncDataResponse =
7
7
  new Struct({ littleEndian: true })
8
8
  .uint32('dataLength')
9
- .arrayBuffer('data', { lengthField: 'dataLength' })
9
+ .uint8Array('data', { lengthField: 'dataLength' })
10
10
  .extra({ id: AdbSyncResponseId.Data as const });
11
11
 
12
12
  const ResponseTypes = {
@@ -14,21 +14,30 @@ const ResponseTypes = {
14
14
  [AdbSyncResponseId.Done]: new AdbSyncDoneResponse(AdbSyncDataResponse.size),
15
15
  };
16
16
 
17
- export async function* adbSyncPull(
17
+ export function adbSyncPull(
18
18
  stream: AdbBufferedStream,
19
+ writer: WritableStreamDefaultWriter<Uint8Array>,
19
20
  path: string,
20
- ): AsyncGenerator<ArrayBuffer, void, void> {
21
- await adbSyncWriteRequest(stream, AdbSyncRequestId.Receive, path);
22
- while (true) {
23
- const response = await adbSyncReadResponse(stream, ResponseTypes);
24
- switch (response.id) {
25
- case AdbSyncResponseId.Data:
26
- yield response.data!;
27
- break;
28
- case AdbSyncResponseId.Done:
29
- return;
30
- default:
31
- throw new Error('Unexpected response id');
21
+ ): ReadableStream<Uint8Array> {
22
+ return new ReadableStream<Uint8Array>({
23
+ async start() {
24
+ await adbSyncWriteRequest(writer, AdbSyncRequestId.Receive, path);
25
+ },
26
+ async pull(controller) {
27
+ const response = await adbSyncReadResponse(stream, ResponseTypes);
28
+ switch (response.id) {
29
+ case AdbSyncResponseId.Data:
30
+ controller.enqueue(response.data!);
31
+ break;
32
+ case AdbSyncResponseId.Done:
33
+ controller.close();
34
+ break;
35
+ default:
36
+ throw new Error('Unexpected response id');
37
+ }
32
38
  }
33
- }
39
+ }, {
40
+ highWaterMark: 16 * 1024,
41
+ size(chunk) { return chunk.byteLength; }
42
+ });
34
43
  }
@@ -1,9 +1,8 @@
1
1
  import Struct from '@yume-chan/struct';
2
- import { AdbBufferedStream } from '../../stream';
3
- import { chunkArrayLike, chunkAsyncIterable } from '../../utils';
4
- import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
5
- import { adbSyncReadResponse, AdbSyncResponseId } from './response';
6
- import { LinuxFileType } from './stat';
2
+ import { AdbBufferedStream, ChunkStream, pipeFrom, WritableStream, WritableStreamDefaultWriter } from '../../stream/index.js';
3
+ import { AdbSyncRequestId, adbSyncWriteRequest } from './request.js';
4
+ import { adbSyncReadResponse, AdbSyncResponseId } from './response.js';
5
+ import { LinuxFileType } from './stat.js';
7
6
 
8
7
  export const AdbSyncOkResponse =
9
8
  new Struct({ littleEndian: true })
@@ -13,34 +12,30 @@ const ResponseTypes = {
13
12
  [AdbSyncResponseId.Ok]: AdbSyncOkResponse,
14
13
  };
15
14
 
16
- export const AdbSyncMaxPacketSize = 64 * 1024;
15
+ export const ADB_SYNC_MAX_PACKET_SIZE = 64 * 1024;
17
16
 
18
- export async function adbSyncPush(
17
+ export function adbSyncPush(
19
18
  stream: AdbBufferedStream,
19
+ writer: WritableStreamDefaultWriter<Uint8Array>,
20
20
  filename: string,
21
- content: ArrayLike<number> | ArrayBufferLike | AsyncIterable<ArrayBuffer>,
22
21
  mode: number = (LinuxFileType.File << 12) | 0o666,
23
22
  mtime: number = (Date.now() / 1000) | 0,
24
- packetSize: number = AdbSyncMaxPacketSize,
25
- onProgress?: (uploaded: number) => void,
26
- ): Promise<void> {
27
- const pathAndMode = `${filename},${mode.toString()}`;
28
- await adbSyncWriteRequest(stream, AdbSyncRequestId.Send, pathAndMode);
29
-
30
- let chunkReader: Iterable<ArrayBuffer> | AsyncIterable<ArrayBuffer>;
31
- if ('length' in content || 'byteLength' in content) {
32
- chunkReader = chunkArrayLike(content, packetSize);
33
- } else {
34
- chunkReader = chunkAsyncIterable(content, packetSize);
35
- }
36
-
37
- let uploaded = 0;
38
- for await (const buffer of chunkReader) {
39
- await adbSyncWriteRequest(stream, AdbSyncRequestId.Data, buffer);
40
- uploaded += buffer.byteLength;
41
- onProgress?.(uploaded);
42
- }
43
-
44
- await adbSyncWriteRequest(stream, AdbSyncRequestId.Done, mtime);
45
- await adbSyncReadResponse(stream, ResponseTypes);
23
+ packetSize: number = ADB_SYNC_MAX_PACKET_SIZE,
24
+ ): WritableStream<Uint8Array> {
25
+ return pipeFrom(
26
+ new WritableStream<Uint8Array>({
27
+ async start() {
28
+ const pathAndMode = `${filename},${mode.toString()}`;
29
+ await adbSyncWriteRequest(writer, AdbSyncRequestId.Send, pathAndMode);
30
+ },
31
+ async write(chunk) {
32
+ await adbSyncWriteRequest(writer, AdbSyncRequestId.Data, chunk);
33
+ },
34
+ async close() {
35
+ await adbSyncWriteRequest(writer, AdbSyncRequestId.Done, mtime);
36
+ await adbSyncReadResponse(stream, ResponseTypes);
37
+ },
38
+ }),
39
+ new ChunkStream(packetSize)
40
+ );
46
41
  }
@@ -1,6 +1,6 @@
1
1
  import Struct from '@yume-chan/struct';
2
- import type { AdbBufferedStream } from '../../stream';
3
- import { encodeUtf8 } from "../../utils";
2
+ import type { WritableStreamDefaultWriter } from "../../stream/index.js";
3
+ import { encodeUtf8 } from "../../utils/index.js";
4
4
 
5
5
  export enum AdbSyncRequestId {
6
6
  List = 'LIST',
@@ -21,14 +21,14 @@ export const AdbSyncNumberRequest =
21
21
  export const AdbSyncDataRequest =
22
22
  new Struct({ littleEndian: true })
23
23
  .fields(AdbSyncNumberRequest)
24
- .arrayBuffer('data', { lengthField: 'arg' });
24
+ .uint8Array('data', { lengthField: 'arg' });
25
25
 
26
26
  export async function adbSyncWriteRequest(
27
- stream: AdbBufferedStream,
27
+ writer: WritableStreamDefaultWriter<Uint8Array>,
28
28
  id: AdbSyncRequestId | string,
29
- value: number | string | ArrayBuffer
29
+ value: number | string | Uint8Array
30
30
  ): Promise<void> {
31
- let buffer: ArrayBuffer;
31
+ let buffer: Uint8Array;
32
32
  if (typeof value === 'number') {
33
33
  buffer = AdbSyncNumberRequest.serialize({
34
34
  id,
@@ -45,5 +45,5 @@ export async function adbSyncWriteRequest(
45
45
  data: value,
46
46
  });
47
47
  }
48
- await stream.write(buffer);
48
+ await writer.write(buffer);
49
49
  }