@cldmv/droidsock 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,42 @@
1
1
  # DroidSock
2
2
 
3
+ <div align="center">
4
+ <img src="https://github.com/CLDMV/droidsock/raw/HEAD/images/droidsock-banner.jpg" alt="DroidSock Banner">
5
+ </div>
6
+
3
7
  A complete, from-scratch implementation of the Android Debug Bridge (ADB) protocol in Node.js. This library provides full ADB functionality including device connection, RSA authentication, shell command execution, and file transfers - eliminating clicking sounds on Android TV devices!
4
8
 
5
- ## Features
9
+ [![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] [![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] [![npm last update]][npm_last_update_url] [![coverage]][coverage_url]
10
+
11
+ [![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url]
12
+
13
+ > [!NOTE]
14
+ > **Current status:**
15
+ >
16
+ > - **Shell + streaming**: Stable - command execution, interactive shells, and log/process streaming all work over the real ADB protocol.
17
+ > - **File transfer**: `mkdir`/`remove`/`move`/`copy`/`chmod`/`diskUsage`/`find` work today via shell commands. `push`/`pull`/`list`/`stat` (the SYNC sub-protocol) are not implemented yet.
18
+
19
+ ---
20
+
21
+ ## ✨ What's New
22
+
23
+ ### Latest: v1.0.0 (September 2026)
24
+
25
+ - **First stable release** - a real Vitest test suite with measured coverage, the full CLDMV v4 CI/release pipeline, a real `dist/` build, and an API surface that's been reviewed rather than just grown.
26
+ - **Breaking**: the default export is now itself the callable quick path (`await droidsock()`); the old top-level `connect()`/`listDevices()` exports are gone.
27
+ - [View full v1.0.0 Changelog](./docs/changelog/v1/v1.0.0.md)
28
+
29
+ 📚 **For complete version history and detailed release notes, see [docs/changelog/](./docs/changelog/) folder.**
6
30
 
7
- - ✅ **Complete ADB Protocol**: Full implementation from TCP connection to high-level APIs
8
- - ✅ **RSA Authentication**: Automatic key generation and ADB-specific formatting
9
- - **Stream Multiplexing**: Multiple concurrent operations over single connection
31
+ ---
32
+
33
+ ## 🚀 Key Features
34
+
35
+ - ✅ **Complete ADB Protocol**: TCP connection, CNXN/AUTH handshake, and stream multiplexing implemented from scratch
36
+ - ✅ **RSA Authentication**: Automatic key generation and ADB-specific signature/public-key formatting
37
+ - ✅ **Stream Multiplexing**: Multiple concurrent operations over a single connection
10
38
  - ✅ **Shell Commands**: Execute commands, stream output, interactive sessions
11
- - ✅ **File Transfers**: Push/pull files with SYNC protocol
39
+ - ✅ **Shell-Based File Operations**: `mkdir`, `remove`, `move`, `copy`, `chmod`, `diskUsage`, `find`
12
40
  - ✅ **Device Discovery**: Support for multiple devices via configuration
13
41
  - ✅ **Error Handling**: Robust error handling and connection recovery
14
42
 
@@ -21,36 +49,29 @@ npm install @cldmv/droidsock
21
49
  ## Quick Start
22
50
 
23
51
  ```javascript
24
- import DroidSock from "@cldmv/droidsock";
52
+ import droidsock from "@cldmv/droidsock";
25
53
 
26
- // Create client
27
- const client = new DroidSock({
28
- host: "10.6.0.108", // Device IP
29
- port: 5555 // ADB port
30
- });
54
+ // Create the API instance
55
+ const api = await droidsock();
31
56
 
32
- // Connect
33
- await client.connect();
57
+ // Connect to a device
58
+ const device = await api.device.connect("10.6.0.108", 5555);
34
59
 
35
- // Execute shell command
36
- const output = await client.shell("ls -la");
60
+ // Execute a shell command
61
+ const output = await device.shell("ls -la");
37
62
  console.log(output);
38
63
 
39
- // Get device info
40
- const model = await client.getModel();
41
- const version = await client.getAndroidVersion();
42
-
43
- // File operations
44
- await client.push("./local-file.txt", "/sdcard/remote-file.txt");
45
- await client.pull("/sdcard/remote-file.txt", "./local-file.txt");
64
+ // Convenience getters
65
+ const model = await device.getModel();
66
+ const version = await device.getAndroidVersion();
46
67
 
47
68
  // Stream commands
48
- const logcat = client.logcat({
69
+ const logcat = device.logcat({
49
70
  onData: (data) => console.log(data)
50
71
  });
51
72
 
52
73
  // Clean up
53
- client.disconnect();
74
+ device.disconnect();
54
75
  ```
55
76
 
56
77
  ## Device Configuration
@@ -77,23 +98,22 @@ Use the `references/devices.json` file to configure your devices:
77
98
 
78
99
  ## API Reference
79
100
 
80
- ### DroidSock
101
+ ### droidsock(options)
81
102
 
82
- Main client class for ADB operations.
103
+ The default export, and the quick path - creates a DroidSock API instance. `options.mode` (`"eager"` or `"lazy"`, default `"eager"`), `options.context`, and `options.config` are all optional. Also available under the explicit name `createDroidSock` (`import { createDroidSock } from "@cldmv/droidsock"`) for callers who prefer it - both names are the exact same function.
83
104
 
84
- #### Constructor Options
105
+ #### device.connect(host, port, options)
85
106
 
86
107
  - `host`: Device IP address
87
108
  - `port`: ADB port (default: 5555)
88
- - `keyDir`: Directory for RSA keys (default: ~/.adb)
109
+ - `options.keyDir`: Directory for RSA keys (default: `~/.adb`)
89
110
 
90
- #### Methods
111
+ Returns a device object with the methods below.
91
112
 
92
113
  ##### Connection
93
114
 
94
- - `connect()`: Connect to device
95
- - `disconnect()`: Disconnect from device
96
115
  - `isConnected()`: Check connection status
116
+ - `disconnect()`: Disconnect from device
97
117
 
98
118
  ##### Shell Commands
99
119
 
@@ -115,10 +135,8 @@ Main client class for ADB operations.
115
135
 
116
136
  ##### File Operations
117
137
 
118
- - `push(localPath, remotePath, options)`: Push file to device
119
- - `pull(remotePath, localPath, options)`: Pull file from device
120
- - `list(remotePath)`: List directory contents
121
- - `stat(remotePath)`: Get file/directory stats
138
+ - `push(localPath, remotePath, options)`: **Not yet implemented** - throws
139
+ - `pull(remotePath, localPath, options)`: **Not yet implemented** - throws
122
140
 
123
141
  ## Examples
124
142
 
@@ -147,15 +165,15 @@ node examples/streaming-example.mjs files
147
165
 
148
166
  ## Architecture
149
167
 
150
- The implementation consists of several layers:
168
+ `src/droidsock.mjs` composes the layers below into a single api tree via [`@cldmv/slothlet`](https://github.com/CLDMV/slothlet):
151
169
 
152
- 1. **Packet Layer** (`packet.mjs`): Low-level ADB packet creation/parsing
153
- 2. **Authentication Layer** (`auth.mjs`): RSA key management and ADB auth
154
- 3. **Connection Layer** (`connection.mjs`): TCP connection and auth flow
155
- 4. **Stream Layer** (`stream.mjs`): Service multiplexing and data flow
156
- 5. **Shell Layer** (`shell.mjs`): Command execution APIs
157
- 6. **SYNC Layer** (`sync.mjs`): File transfer protocol
158
- 7. **Client Layer** (`adb.mjs`): Unified high-level API
170
+ 1. **Connection Layer** (`src/api/connection.mjs`): TCP socket + CNXN/AUTH handshake
171
+ 2. **Authentication Layer** (`src/api/auth.mjs`): RSA key management and ADB signature/public-key formatting
172
+ 3. **Stream Layer** (`src/api/stream.mjs`): ADB stream multiplexing (OPEN/WRTE/OKAY/CLSE)
173
+ 4. **Shell Layer** (`src/api/shell.mjs`): Command execution, streaming, and interactive shell APIs
174
+ 5. **Files Layer** (`src/api/files.mjs`): Shell-based file operations (SYNC-protocol `push`/`pull`/`list`/`stat` not yet implemented)
175
+ 6. **Device Layer** (`src/api/device.mjs`): High-level per-device API composing the layers above
176
+ 7. **Config / Log Layers** (`src/api/config.mjs`, `src/api/log.mjs`): Shared configuration and logging
159
177
 
160
178
  ## Protocol Implementation Details
161
179
 
@@ -192,6 +210,7 @@ All ADB packets follow a 24-byte header + optional data format:
192
210
  - SEND/RECV commands for push/pull
193
211
  - DATA packets for file chunks
194
212
  - DONE packets signal completion
213
+ - Not yet implemented - see [Current status](#-key-features) above
195
214
 
196
215
  ## Troubleshooting
197
216
 
@@ -225,8 +244,25 @@ The implementation is based on:
225
244
 
226
245
  ## License
227
246
 
228
- MIT License - see package.json for details.
247
+ Apache-2.0 - see [LICENSE](LICENSE) for details.
229
248
 
230
249
  ## Contributing
231
250
 
232
251
  This is a complete implementation of the ADB protocol. For improvements or bug fixes, please submit issues or pull requests.
252
+
253
+ [npm version]: https://img.shields.io/npm/v/%40cldmv%2Fdroidsock.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
254
+ [npm_version_url]: https://www.npmjs.com/package/@cldmv/droidsock
255
+ [npm downloads]: https://img.shields.io/npm/dm/%40cldmv%2Fdroidsock.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
256
+ [npm_downloads_url]: https://www.npmjs.com/package/@cldmv/droidsock
257
+ [github downloads]: https://img.shields.io/github/downloads/CLDMV/droidsock/total?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
258
+ [github_downloads_url]: https://github.com/CLDMV/droidsock/releases
259
+ [last commit]: https://img.shields.io/github/last-commit/CLDMV/droidsock?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
260
+ [last_commit_url]: https://github.com/CLDMV/droidsock/commits
261
+ [npm last update]: https://img.shields.io/npm/last-update/%40cldmv%2Fdroidsock?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
262
+ [npm_last_update_url]: https://www.npmjs.com/package/@cldmv/droidsock
263
+ [coverage]: https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FCLDMV%2Fdroidsock%2Fbadges%2Fcoverage.json&style=for-the-badge&logo=vitest&logoColor=white
264
+ [coverage_url]: https://github.com/CLDMV/droidsock/blob/badges/coverage.json
265
+ [contributors]: https://img.shields.io/github/contributors/CLDMV/droidsock.svg?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
266
+ [contributors_url]: https://github.com/CLDMV/droidsock/graphs/contributors
267
+ [sponsor shinrai]: https://img.shields.io/github/sponsors/shinrai?style=for-the-badge&logo=githubsponsors&logoColor=white&labelColor=EA4AAA&label=Sponsor
268
+ [sponsor_url]: https://github.com/sponsors/shinrai
package/devcheck.mjs CHANGED
@@ -6,9 +6,9 @@
6
6
  * @Email: <Shinrai@users.noreply.github.com>
7
7
  * -----
8
8
  * @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
9
- * @Last modified time: 2025-11-21 14:50:46 -08:00 (1763765446)
9
+ * @Last modified time: 2026-08-30 16:02:20 -07:00 (1788130940)
10
10
  * -----
11
- * @Copyright: Copyright (c) 2013-2025 Catalyzed Motivation Inc. All rights reserved.
11
+ * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
12
12
  */
13
13
 
14
14
  import { existsSync } from "node:fs";
@@ -34,27 +34,28 @@ const isCI = !!(
34
34
 
35
35
  if (existsSync(srcPath) && !isCI) {
36
36
  // if (existsSync(srcPath) && !existsSync(distPath)) {
37
- const nodeEnv = process.env.NODE_ENV?.toLowerCase();
38
- const hasNodeOptions = process.env.NODE_OPTIONS?.includes("--conditions=development");
37
+ // NODE_ENV plays no part in Node's conditional-exports resolution - only the
38
+ // "--conditions=droidsock-dev" condition (via NODE_OPTIONS) actually routes
39
+ // exports["./main"] to src/ instead of dist/. NODE_ENV=development alone
40
+ // would previously pass this check while still resolving to dist/.
41
+ const hasNodeOptions = process.env.NODE_OPTIONS?.includes("--conditions=droidsock-dev");
39
42
 
40
- if (!nodeEnv || (!["dev", "development"].includes(nodeEnv) && !hasNodeOptions)) {
43
+ if (!hasNodeOptions) {
41
44
  console.error("❌ Development environment not properly configured!");
42
- console.error("📁 Source folder detected but NODE_ENV/NODE_OPTIONS not set for development.");
45
+ console.error("📁 Source folder detected but NODE_OPTIONS is not set for development.");
43
46
  console.error("");
44
47
  console.error("🔧 To fix this, run one of these commands:");
45
48
  console.error(" Windows (cmd):");
46
- console.error(" set NODE_ENV=development");
47
- console.error(" set NODE_OPTIONS=--conditions=development");
49
+ console.error(" set NODE_OPTIONS=--conditions=droidsock-dev");
48
50
  console.error("");
49
51
  console.error(" Windows (PowerShell):");
50
- console.error(" $env:NODE_ENV='development'");
51
- console.error(" $env:NODE_OPTIONS='--conditions=development'");
52
+ console.error(" $env:NODE_OPTIONS='--conditions=droidsock-dev'");
52
53
  console.error("");
53
54
  console.error(" Unix/Linux/macOS:");
54
- console.error(" export NODE_ENV=development");
55
- console.error(" export NODE_OPTIONS=--conditions=development");
55
+ console.error(" export NODE_OPTIONS=--conditions=droidsock-dev");
56
56
  console.error("");
57
57
  console.error("💡 This ensures this module loads from src/ instead of dist/ for development.");
58
+ console.error(" (NODE_ENV is not checked here - it has no effect on this resolution.)");
58
59
  console.error("🚀 CI environments automatically skip this check.");
59
60
  process.exit(1);
60
61
  }
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";import crypto from"node:crypto";import fs from"node:fs";import path from"node:path";import os from"node:os";function getKeys(keyDir){const adbDir=keyDir||path.join(os.homedir(),".adb");const privateKeyPath=path.join(adbDir,"adbkey");const publicKeyPath=path.join(adbDir,"adbkey.pub");if(!fs.existsSync(adbDir)){fs.mkdirSync(adbDir,{recursive:true})}if(fs.existsSync(privateKeyPath)&&fs.existsSync(publicKeyPath)){const privateKey=fs.readFileSync(privateKeyPath,"utf8");const adbPublicKey=fs.readFileSync(publicKeyPath,"utf8").trim();const keyObject=crypto.createPrivateKey(privateKey);const publicKey=crypto.createPublicKey(keyObject).export({type:"spki",format:"pem"});return{privateKey,publicKey,adbPublicKey}}return generateKeys(2048,adbDir)}function generateKeys(keySize=2048,saveDir=null){const{publicKey,privateKey}=crypto.generateKeyPairSync("rsa",{modulusLength:keySize,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});const adbPublicKey=formatAdbPublicKey(publicKey);if(saveDir){if(!fs.existsSync(saveDir)){fs.mkdirSync(saveDir,{recursive:true})}const privateKeyPath=path.join(saveDir,"adbkey");const publicKeyPath=path.join(saveDir,"adbkey.pub");fs.writeFileSync(privateKeyPath,privateKey);fs.writeFileSync(publicKeyPath,adbPublicKey)}return{privateKey,publicKey,adbPublicKey}}function sign(token,privateKey){self.log.debug("[DEBUG] AOSP-STYLE ADB SIGNATURE (token as digest):");self.log.debug("[DEBUG] Token:",token.toString("hex"),`(${token.length} bytes)`);if(!Buffer.isBuffer(token)){token=Buffer.from(token)}if(token.length!==20){throw new Error(`Token must be 20 bytes, got ${token.length}`)}const DIGESTINFO_SHA1_PREFIX=Buffer.from([48,33,48,9,6,5,43,14,3,2,26,5,0,4,20]);const digestInfo=Buffer.concat([DIGESTINFO_SHA1_PREFIX,token]);self.log.debug("[DEBUG] DigestInfo:",digestInfo.toString("hex"));const keyObject=crypto.createPrivateKey(privateKey);let keySize=keyObject.asymmetricKeySize;if(keySize===void 0){const keyDetails=keyObject.asymmetricKeyDetails||{};if(keyDetails.modulusLength){keySize=Math.ceil(keyDetails.modulusLength/8)}else{keySize=getKeySizeFromPem(privateKey)}}self.log.debug("[DEBUG] Key size:",keySize,"bytes");const paddedBlock=buildPkcs1v15Block(digestInfo,keySize);self.log.debug("[DEBUG] PKCS#1 v1.5 block length:",paddedBlock.length,"bytes");self.log.debug("[DEBUG] Block starts:",paddedBlock.slice(0,16).toString("hex")+"...");self.log.debug("[DEBUG] Block ends: ..."+paddedBlock.slice(-16).toString("hex"));const signature=crypto.privateEncrypt({key:keyObject,padding:crypto.constants.RSA_NO_PADDING},paddedBlock);self.log.debug("[DEBUG] Signature length:",signature.length,"bytes");self.log.debug("[DEBUG] Signature starts with:",signature.slice(0,8).toString("hex"));return signature}function buildPkcs1v15Block(data,keySize){self.log.debug("[DEBUG] buildPkcs1v15Block - data length:",data.length,"keySize:",keySize,"keySize type:",typeof keySize);if(isNaN(keySize)||keySize<=0){throw new Error(`Invalid key size: ${keySize}`)}const paddingLength=keySize-data.length-3;self.log.debug("[DEBUG] Padding length:",paddingLength);if(paddingLength<8){throw new Error(`Key too small for data - keySize: ${keySize}, dataLength: ${data.length}, paddingLength: ${paddingLength}`)}const padding=Buffer.alloc(paddingLength,255);return Buffer.concat([Buffer.from([0,1]),padding,Buffer.from([0]),data])}function validateAuth(token,privateKey){try{if(!Buffer.isBuffer(token)||token.length!==20){return false}if(typeof privateKey!=="string"||!privateKey.includes("-----BEGIN PRIVATE KEY-----")){return false}sign(token,privateKey);return true}catch{return false}}function formatAdbPublicKey(publicKey){self.log.debug("[DEBUG] Creating ADB public key format...");const pubKey=crypto.createPublicKey(publicKey);const jwk=pubKey.export({format:"jwk"});const n=base64UrlToBuf(jwk.n);const e=base64UrlToBuf(jwk.e);self.log.debug("[DEBUG] Exponent length:",e.length,"bytes, first byte: 0x"+e[0].toString(16).padStart(2,"0"));self.log.debug("[DEBUG] Modulus length:",n.length,"bytes, first byte: 0x"+n[0].toString(16).padStart(2,"0"));const eStripped=e[0]===0?e.slice(1):e;const nStripped=n[0]===0?n.slice(1):n;self.log.debug("[DEBUG] After stripping - Exponent:",eStripped.length,"bytes, Modulus:",nStripped.length,"bytes");const parts=[];writeSshString(parts,Buffer.from("ssh-rsa"));writeSshString(parts,eStripped);writeSshString(parts,nStripped);const sshBlob=Buffer.concat(parts);const base64Key=sshBlob.toString("base64");self.log.debug("[DEBUG] SSH blob length:",sshBlob.length,"bytes");self.log.debug("[DEBUG] Base64 length:",base64Key.length,"chars");self.log.debug("[DEBUG] Base64 preview:",base64Key.substring(0,50)+"...");const hostname=os.hostname()||"unknown";const username=os.userInfo().username||"user";const comment=`${username}@${hostname}`;const result=`${base64Key} ${comment}\0`;self.log.debug("[DEBUG] Final ADB key length:",result.length,"chars");return result}function base64UrlToBuf(b64url){const b64=b64url.replace(/-/g,"+").replace(/_/g,"/");const pad=b64.length%4;const padded=pad?b64+"=".repeat(4-pad):b64;return Buffer.from(padded,"base64")}function writeSshString(bufs,data){const len=Buffer.alloc(4);len.writeUInt32BE(data.length,0);bufs.push(len,data)}function getKeySizeFromPem(privateKey){try{const base64=privateKey.replace(/-----BEGIN PRIVATE KEY-----/,"").replace(/-----END PRIVATE KEY-----/,"").replace(/\s/g,"");const derBuffer=Buffer.from(base64,"base64");if(derBuffer.length>1e3){return 512}else{return 256}}catch(error){self.log.debug("[DEBUG] Failed to parse key size from PEM, defaulting to 256 bytes:",error.message);return 256}}export{formatAdbPublicKey,generateKeys,getKeys,sign,validateAuth};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ const DEFAULT_CONFIG={host:"127.0.0.1",port:5555,timeout:1e4,retryAttempts:3,retryDelay:1e3,keyDir:null,autoGenerateKeys:true,keySize:2048,debug:false,verbose:false,silent:false,debugArrowSent:">>>>",debugArrowReceived:"<<<<",streamTimeout:3e4,maxStreams:10,shellTimeout:3e4,shellEncoding:"utf8",fileTimeout:6e4,chunkSize:65536,emitEvents:true,eventPrefix:"adb",bufferSize:1024*1024,keepAlive:true,keepAliveInterval:3e4};let configInstance=null;function init(options={}){if(!configInstance){configInstance={...DEFAULT_CONFIG,...options}}return getApi()}function get(key,defaultValue=void 0){if(!configInstance){init()}const keys=key.split(".");let value=configInstance;for(const k of keys){if(value&&typeof value==="object"&&k in value){value=value[k]}else{return defaultValue}}return value}function set(key,value){if(!configInstance){init()}const keys=key.split(".");let obj=configInstance;for(let i=0;i<keys.length-1;i++){const k=keys[i];if(!(k in obj)||typeof obj[k]!=="object"){obj[k]={}}obj=obj[k]}obj[keys[keys.length-1]]=value}function merge(options){if(!configInstance){init()}const mergeRecursive=(target,source,path="")=>{for(const[key,value]of Object.entries(source)){const fullPath=path?`${path}.${key}`:key;if(typeof value==="object"&&value!==null&&!Array.isArray(value)){if(!(key in target)||typeof target[key]!=="object"){target[key]={}}mergeRecursive(target[key],value,fullPath)}else{target[key]=value}}};mergeRecursive(configInstance,options)}function reset(){configInstance={...DEFAULT_CONFIG}}function all(){return configInstance?{...configInstance}:{...DEFAULT_CONFIG}}function getApi(){return{init,get,set,merge,reset,all}}function getDefaults(){return{...DEFAULT_CONFIG}}function validateConfig(config){const errors=[];if(config.port&&(typeof config.port!=="number"||config.port<1||config.port>65535)){errors.push("port must be a number between 1 and 65535")}if(config.timeout&&(typeof config.timeout!=="number"||config.timeout<0)){errors.push("timeout must be a non-negative number")}const timeouts=["streamTimeout","shellTimeout","fileTimeout"];for(const timeout of timeouts){if(config[timeout]&&(typeof config[timeout]!=="number"||config[timeout]<0)){errors.push(`${timeout} must be a non-negative number`)}}const bufferSizes=["bufferSize","chunkSize"];for(const size of bufferSizes){if(config[size]&&(typeof config[size]!=="number"||config[size]<1024)){errors.push(`${size} must be a number >= 1024`)}}return{valid:errors.length===0,errors}}export{all,get,getApi,getDefaults,init,merge,reset,set,validateConfig};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";import net from"node:net";const ADB_PROTOCOL_VERSION=16777216;const ADB_MAX_PAYLOAD=4096;const MSG_CNXN=1314410051;const MSG_AUTH=1213486401;const MSG_OKAY=1497451343;async function create(options){const{host,port,privateKey,adbPublicKey}=options;const socket=new net.Socket;const connection={socket,host,port,authorized:false,connected:false,onUnhandledPacket:null,disconnect:()=>{if(socket&&!socket.destroyed){self.log.debug(`Disconnecting from ${host}:${port}`);socket.destroy()}connection.connected=false;connection.authorized=false}};return new Promise((resolve,reject)=>{const timeout=setTimeout(()=>{socket.destroy();reject(new Error("Connection timeout"))},1e4);socket.connect(port,host,async()=>{try{self.log.debug(`Connected to ${host}:${port}, sending CNXN message...`);const systemIdentity="device::";const cnxnPayload=Buffer.from(systemIdentity);await sendMessage(socket,MSG_CNXN,ADB_PROTOCOL_VERSION,ADB_MAX_PAYLOAD,cnxnPayload);self.log.debug(`${self.config.get("debugArrowSent")} CNXN message sent, waiting for AUTH response...`);const authMessage=await receiveMessage(socket);self.log.debug(`${self.config.get("debugArrowReceived")} Received AUTH message, command:`,authMessage.command.toString(16));if(authMessage.command!==MSG_AUTH){throw new Error(`Expected AUTH message (0x${MSG_AUTH.toString(16)}), got 0x${authMessage.command.toString(16)}`)}self.log.debug(`${self.config.get("debugArrowReceived")} AUTH message received, token length:`,authMessage.data.length);const token=authMessage.data;const signature=await self.auth.sign(token,privateKey);await sendMessage(socket,MSG_AUTH,2,0,signature);const pubKeyBuffer=Buffer.from(adbPublicKey);await sendMessage(socket,MSG_AUTH,3,0,pubKeyBuffer);const okayMessage=await receiveMessage(socket);self.log.debug(`${self.config.get("debugArrowReceived")} After auth, received command:`,okayMessage.command.toString(16));self.log.debug("Expected CNXN:",MSG_CNXN.toString(16),"or OKAY:",MSG_OKAY.toString(16));if(okayMessage.command===MSG_CNXN){const cnxnPayload2=okayMessage.data.toString().replace(/\0/g,"");self.log.debug(`${self.config.get("debugArrowReceived")} Device sent CNXN response - authentication successful!`);self.log.debug(`${self.config.get("debugArrowReceived")} CNXN payload:`,cnxnPayload2);const featuresMatch=cnxnPayload2.match(/features=([^;]+)/);connection.deviceFeatures=featuresMatch?featuresMatch[1].split(","):[];connection.authorized=true;connection.connected=true;self.log.debug("Authentication successful!")}else if(okayMessage.command===MSG_OKAY){self.log.debug(`${self.config.get("debugArrowReceived")} Device sent OKAY response - authentication successful!`);connection.authorized=true;connection.connected=true}else{throw new Error(`Authentication failed - expected CNXN (${MSG_CNXN.toString(16)}) or OKAY (${MSG_OKAY.toString(16)}), got ${okayMessage.command.toString(16)}`)}clearTimeout(timeout);socket.on("data",data=>{if(connection.onUnhandledPacket){connection.onUnhandledPacket(data)}});resolve(connection)}catch(error){clearTimeout(timeout);self.log.debug("Connection error:",error.message);reject(error)}});socket.on("error",error=>{clearTimeout(timeout);self.log.debug("Socket error:",error.message);reject(error)})})}async function sendMessage(socket,command,arg0,arg1,data=Buffer.alloc(0)){const header=Buffer.alloc(24);let offset=0;header.writeUInt32LE(command,offset);offset+=4;header.writeUInt32LE(arg0,offset);offset+=4;header.writeUInt32LE(arg1,offset);offset+=4;header.writeUInt32LE(data.length,offset);offset+=4;header.writeUInt32LE(checksum(data),offset);offset+=4;header.writeUInt32LE((command^4294967295)>>>0,offset);socket.write(header);if(data.length>0){socket.write(data)}}function receiveMessage(socket){return new Promise((resolve,reject)=>{let headerReceived=false;let expectedDataLength=0;let receivedHeader=null;let receivedData=Buffer.alloc(0);const onData=chunk=>{if(!headerReceived){if(chunk.length>=24){receivedHeader=chunk.slice(0,24);self.log.debug("Header bytes:",Array.from(receivedHeader).map(b=>b.toString(16).padStart(2,"0")).join(" "));try{expectedDataLength=receivedHeader.readUInt32LE(12);self.log.debug("Raw data length from header:",expectedDataLength)}catch(readError){reject(new Error(`Failed to read data length from header: ${readError.message}`));return}if(isNaN(expectedDataLength)||expectedDataLength<0||expectedDataLength>1024*1024){reject(new Error(`Invalid data length: ${expectedDataLength} (header: ${receivedHeader.toString("hex")})`));return}headerReceived=true;if(chunk.length>24){receivedData=Buffer.concat([receivedData,chunk.slice(24)])}}}else{receivedData=Buffer.concat([receivedData,chunk])}if(headerReceived&&receivedData.length>=expectedDataLength){socket.removeListener("data",onData);try{const command=receivedHeader.readUInt32LE(0);const arg0=receivedHeader.readUInt32LE(4);const arg1=receivedHeader.readUInt32LE(8);const dataLength=receivedHeader.readUInt32LE(12);const headerChecksum=receivedHeader.readUInt32LE(16);const magic=receivedHeader.readUInt32LE(20);self.log.debug("Parsed header - command:",command.toString(16),"arg0:",arg0,"arg1:",arg1,"length:",dataLength,"checksum:",headerChecksum,"magic:",magic.toString(16));const expectedMagic=(command^4294967295)>>>0;if(magic!==expectedMagic){self.log.debug("Magic mismatch - expected:",expectedMagic.toString(16),"got:",magic.toString(16))}const data=receivedData.slice(0,expectedDataLength);if(data.length>0){const calculatedChecksum=checksum(data);self.log.debug("Data checksum - expected:",headerChecksum,"calculated:",calculatedChecksum)}resolve({command,arg0,arg1,data})}catch(parseError){reject(new Error(`Failed to parse message header: ${parseError.message}`))}}};socket.on("data",onData);socket.on("error",reject)})}function checksum(data){let sum=0;for(let i=0;i<data.length;i++){sum=sum+data[i]>>>0}return sum}export{create};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";const connections=new Map;async function connect(host,port=5555,options={}){const deviceId=`${host}:${port}`;if(connections.has(deviceId)){const existing=connections.get(deviceId);if(existing.isConnected()){return existing}else{connections.delete(deviceId)}}const keys=await self.auth.getKeys(options.keyDir);const connection=await self.connection.create({host,port,publicKey:keys.publicKey,privateKey:keys.privateKey,adbPublicKey:keys.adbPublicKey});const streamManager=await self.stream.create(connection.socket);connection.onUnhandledPacket=packet=>streamManager.handlePacket(packet);const device={host,port,deviceId,connection,streamManager,isConnected:()=>connection&&connection.connected,disconnect:()=>{if(connection){connection.disconnect();connections.delete(deviceId)}},shell:async(command,shellOptions={})=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return await self.shell.execute(connection.socket,streamManager,command,{...shellOptions,deviceFeatures:connection.deviceFeatures||[]})},startStreamingShell:(command,shellOptions={})=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return self.shell.startStreaming(connection.socket,streamManager,command,shellOptions)},startInteractiveShell:(command,shellOptions={})=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return self.shell.startInteractive(connection.socket,streamManager,command,shellOptions)},push:async(localPath,remotePath,transferOptions={})=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return await self.files.push(connection.socket,streamManager,localPath,remotePath,transferOptions)},pull:async(remotePath,localPath,transferOptions={})=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return await self.files.pull(connection.socket,streamManager,remotePath,localPath,transferOptions)},list:async remotePath=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return await self.files.list(connection.socket,streamManager,remotePath)},stat:async remotePath=>{if(!device.isConnected()){throw new Error("Device not connected")}if(!connection.authorized){throw new Error("Device not authorized. Please accept authorization dialog.")}return await self.files.stat(connection.socket,streamManager,remotePath)},get ls(){return(path=".")=>device.shell(`ls -la "${path}"`)},get pwd(){return()=>device.shell("pwd")},get getprop(){return(prop=null)=>device.shell(prop?`getprop "${prop}"`:"getprop")},get getModel(){return()=>device.shell("getprop ro.product.model")},get getAndroidVersion(){return()=>device.shell("getprop ro.build.version.release")},get getBattery(){return()=>device.shell("dumpsys battery")},get screenshot(){return(filename="/sdcard/screenshot.png")=>device.shell(`screencap -p "${filename}"`)},get logcat(){return(logOptions={})=>device.startStreamingShell("logcat",logOptions)},get top(){return(topOptions={})=>device.startStreamingShell("top -m 10",topOptions)},get keypress(){return key=>device.shell(`input keyevent ${key}`)},get launchApp(){return(packageName,activity="")=>{const activityArg=activity?`/${activity}`:"";return device.shell(`am start -n ${packageName}${activityArg}`)}}};connections.set(deviceId,device);return device}function list(){return Array.from(connections.values()).filter(device=>device.isConnected())}function disconnect(host,port=5555){const deviceId=`${host}:${port}`;const device=connections.get(deviceId);if(device){device.disconnect();return true}return false}function disconnectAll(){let count=0;for(const device of connections.values()){device.disconnect();count++}connections.clear();return count}export{connect,disconnect,disconnectAll,list};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";async function push(___socket,___streamManager,___localPath,___remotePath,___options={}){throw new Error("File push not yet implemented in slothlet structure")}async function pull(___socket,___streamManager,___remotePath,___localPath,___options={}){throw new Error("File pull not yet implemented in slothlet structure")}async function list(___socket,___streamManager,___remotePath){throw new Error("Directory listing not yet implemented in slothlet structure")}async function stat(___socket,___streamManager,___remotePath){throw new Error("File stat not yet implemented in slothlet structure")}async function mkdir(socket,streamManager,remotePath,mode=493){const command=`mkdir -p "${remotePath}" && chmod ${mode.toString(8)} "${remotePath}"`;return await self.shell.execute(socket,streamManager,command)}async function remove(socket,streamManager,remotePath,recursive=false){const flag=recursive?"-rf":"-f";const command=`rm ${flag} "${remotePath}"`;return await self.shell.execute(socket,streamManager,command)}async function move(socket,streamManager,sourcePath,destPath){const command=`mv "${sourcePath}" "${destPath}"`;return await self.shell.execute(socket,streamManager,command)}async function copy(socket,streamManager,sourcePath,destPath,recursive=false){const flag=recursive?"-r":"";const command=`cp ${flag} "${sourcePath}" "${destPath}"`;return await self.shell.execute(socket,streamManager,command)}async function chmod(socket,streamManager,remotePath,mode,recursive=false){const flag=recursive?"-R":"";const command=`chmod ${flag} ${mode.toString(8)} "${remotePath}"`;return await self.shell.execute(socket,streamManager,command)}async function diskUsage(socket,streamManager,path="/"){const command=`df -h "${path}"`;return await self.shell.execute(socket,streamManager,command)}async function find(socket,streamManager,path,pattern,options={}){let command=`find "${path}"`;if(options.maxDepth){command+=` -maxdepth ${options.maxDepth}`}if(options.type){command+=` -type ${options.type}`}command+=` -name "${pattern}"`;return await self.shell.execute(socket,streamManager,command)}export{chmod,copy,diskUsage,find,list,mkdir,move,pull,push,remove,stat};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";function getConfig(){return self.config}function getPrefix(){return getConfig().get("eventPrefix","adb")}function debug(...args){if(getConfig().get("silent"))return;if(!getConfig().get("debug"))return;console.log(`[${getPrefix()}][DEBUG]`,...args)}function verbose(...args){if(getConfig().get("silent"))return;if(!getConfig().get("verbose")&&!getConfig().get("debug"))return;console.log(`[${getPrefix()}][VERBOSE]`,...args)}function info(...args){if(getConfig().get("silent"))return;console.log(`[${getPrefix()}][INFO]`,...args)}function warn(...args){if(getConfig().get("silent"))return;console.warn(`[${getPrefix()}][WARN]`,...args)}function error(...args){if(getConfig().get("silent"))return;console.error(`[${getPrefix()}][ERROR]`,...args)}function child(context){const childPrefix=`[${getPrefix()}][${context}]`;const cfg=getConfig();return{debug:(...args)=>{if(cfg.get("silent"))return;if(!cfg.get("debug"))return;console.log(`${childPrefix}[DEBUG]`,...args)},verbose:(...args)=>{if(cfg.get("silent"))return;if(!cfg.get("verbose")&&!cfg.get("debug"))return;console.log(`${childPrefix}[VERBOSE]`,...args)},info:(...args)=>{if(cfg.get("silent"))return;console.log(`${childPrefix}[INFO]`,...args)},warn:(...args)=>{if(cfg.get("silent"))return;console.warn(`${childPrefix}[WARN]`,...args)},error:(...args)=>{if(cfg.get("silent"))return;console.error(`${childPrefix}[ERROR]`,...args)}}}function getApi(){return{debug,verbose,info,warn,error,child}}export{child,debug,error,getApi,info,verbose,warn};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";async function execute(socket,streamManager,command,options={}){const{timeout=3e4,encoding="utf8",deviceFeatures=[]}=options;return new Promise((resolve,reject)=>{try{let output=Buffer.alloc(0);let streamId=null;let commandTimeout;let commandCompleted=false;let protocolUsed;const supportsShellV2=deviceFeatures.includes("shell_v2");if(supportsShellV2){protocolUsed=`shell,v2:${command}`}else{protocolUsed=`shell:${command}`}if(self.config.get("debug")){self.log.debug(`Executing shell command: ${protocolUsed}`)}const destBuffer=Buffer.from(protocolUsed);const openPacket=Buffer.alloc(24+destBuffer.length);openPacket.writeUInt32LE(1313165391,0);openPacket.writeUInt32LE(12345,4);openPacket.writeUInt32LE(0,8);openPacket.writeUInt32LE(destBuffer.length,12);let checksum=0;for(let i=0;i<destBuffer.length;i++){checksum+=destBuffer[i]}openPacket.writeUInt32LE(checksum&4294967295,16);openPacket.writeUInt32LE(~1313165391>>>0,20);destBuffer.copy(openPacket,24);socket.write(openPacket);commandTimeout=setTimeout(()=>{if(!commandCompleted){commandCompleted=true;socket.removeAllListeners("data");reject(new Error(`Command timeout: ${command}`))}},timeout);let responseBuffer=Buffer.alloc(0);let originalHandlers=[];const existingHandlers=socket.listeners("data");existingHandlers.forEach(handler=>{socket.removeListener("data",handler);originalHandlers.push(handler)});const dataHandler=chunk=>{if(commandCompleted)return;responseBuffer=Buffer.concat([responseBuffer,chunk]);while(responseBuffer.length>=24){const cmd=responseBuffer.readUInt32LE(0);const arg0=responseBuffer.readUInt32LE(4);const arg1=responseBuffer.readUInt32LE(8);const dataLength=responseBuffer.readUInt32LE(12);if(responseBuffer.length<24+dataLength)break;const packetData=dataLength>0?responseBuffer.slice(24,24+dataLength):null;responseBuffer=responseBuffer.slice(24+dataLength);if(self.config.get("debug")){const commandName=cmd===1497451343?"OKAY":cmd===1163154007?"WRTE":cmd===1163086915?"CLSE":`0x${cmd.toString(16)}`;self.log.debug(`${self.config.get("debugArrowReceived")} ${commandName} arg0:${arg0} arg1:${arg1} len:${dataLength}`)}if(cmd===1497451343&&!streamId){streamId=arg1;if(self.config.get("debug")){self.log.debug(`Shell stream opened with ID: ${streamId}`)}}else if(cmd===1163154007&&arg1===12345){if(packetData){output=Buffer.concat([output,packetData]);if(self.config.get("debug")){self.log.debug(`Received ${packetData.length} bytes of output`)}}}else if(cmd===1163086915&&arg1===12345){if(self.config.get("debug")){self.log.debug("Shell stream closed by device")}commandCompleted=true;clearTimeout(commandTimeout);socket.removeListener("data",dataHandler);originalHandlers.forEach(handler=>{socket.on("data",handler)});resolve(output.toString(encoding));return}}};socket.on("data",dataHandler)}catch(error){reject(error)}})}function startStreaming(socket,streamManager,command,options={}){const{onData,onError,onEnd}=options;let stream=null;const control={stop(){if(stream){stream.close();stream=null}}};(async()=>{try{stream=await streamManager.openStream(`shell:${command}`);stream.on("data",data=>{if(onData)onData(data.toString())});stream.on("close",()=>{if(onEnd)onEnd()});stream.on("error",error=>{if(onError)onError(error)})}catch(error){if(onError)onError(error)}})();return control}function startInteractive(socket,streamManager,command,options={}){const{onData,onError,onEnd}=options;let stream=null;const control={async sendInput(input){if(stream){await stream.write(input)}},stop(){if(stream){stream.close();stream=null}}};(async()=>{try{stream=await streamManager.openStream(`shell:${command}`);stream.on("data",data=>{if(onData)onData(data.toString())});stream.on("close",()=>{if(onEnd)onEnd()});stream.on("error",error=>{if(onError)onError(error)})}catch(error){if(onError)onError(error)}})();return control}const commands={ls:async(socket,streamManager,path=".")=>{return await execute(socket,streamManager,`ls -la "${path}"`)},pwd:async(socket,streamManager)=>{return await execute(socket,streamManager,"pwd")},getprop:async(socket,streamManager,prop=null)=>{const cmd=prop?`getprop "${prop}"`:"getprop";return await execute(socket,streamManager,cmd)},getModel:async(socket,streamManager)=>{return await execute(socket,streamManager,"getprop ro.product.model")},getAndroidVersion:async(socket,streamManager)=>{return await execute(socket,streamManager,"getprop ro.build.version.release")},getBattery:async(socket,streamManager)=>{return await execute(socket,streamManager,"dumpsys battery")},screenshot:async(socket,streamManager,filename="/sdcard/screenshot.png")=>{return await execute(socket,streamManager,`screencap -p "${filename}"`)},logcat:(socket,streamManager,options={})=>{return startStreaming(socket,streamManager,"logcat",options)},top:(socket,streamManager,options={})=>{return startStreaming(socket,streamManager,"top -m 10",options)},keypress:async(socket,streamManager,key)=>{return await execute(socket,streamManager,`input keyevent ${key}`)},launchApp:async(socket,streamManager,packageName,activity="")=>{const activityArg=activity?`/${activity}`:"";return await execute(socket,streamManager,`am start -n ${packageName}${activityArg}`)},killApp:async(socket,streamManager,packageName)=>{return await execute(socket,streamManager,`am force-stop ${packageName}`)},installApk:async(socket,streamManager,apkPath,flags=[])=>{const flagsStr=flags.length>0?` ${flags.join(" ")}`:"";return await execute(socket,streamManager,`pm install${flagsStr} "${apkPath}"`)},uninstallApp:async(socket,streamManager,packageName)=>{return await execute(socket,streamManager,`pm uninstall ${packageName}`)}};export{commands,execute,startInteractive,startStreaming};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import{self}from"@cldmv/slothlet/runtime";import{EventEmitter}from"node:events";const MSG_OPEN=1313165391;const MSG_WRTE=1163154007;const MSG_CLSE=1163086915;const MSG_OKAY=1497451343;function getCommandName(command){switch(command){case MSG_OPEN:return"OPEN";case MSG_WRTE:return"WRTE";case MSG_CLSE:return"CLSE";case MSG_OKAY:return"OKAY";default:return`0x${command.toString(16).toUpperCase()}`}}function create(socket){const streams=new Map;let nextStreamId=1;const manager={socket,streams,nextStreamId,async openStream(destination){const localId=nextStreamId++;const stream=new AdbStream(localId,0,socket,manager);streams.set(localId,stream);const destBuffer=Buffer.from(destination);await sendMessage(socket,MSG_OPEN,localId,0,destBuffer);return new Promise((resolve,reject)=>{stream.once("ready",()=>resolve(stream));stream.once("error",reject);setTimeout(()=>{if(!stream.ready){streams.delete(localId);reject(new Error("Stream open timeout"))}},5e3)})},handlePacket(data){let offset=0;while(offset<data.length){if(data.length-offset<24)break;const command=data.readUInt32LE(offset);const arg0=data.readUInt32LE(offset+4);const arg1=data.readUInt32LE(offset+8);const dataLength=data.readUInt32LE(offset+12);if(data.length-offset<24+dataLength)break;const packetData=data.slice(offset+24,offset+24+dataLength);offset+=24+dataLength;const commandName=getCommandName(command);if(self.config.get("debug")){self.log.debug(`${self.config.get("debugArrowReceived")} ${commandName} arg0:${arg0} arg1:${arg1} len:${dataLength}`)}const stream=streams.get(arg1);if(stream){stream.handleMessage(command,arg0,arg1,packetData)}}},closeStream(streamId){const stream=streams.get(streamId);if(stream){stream.close();streams.delete(streamId)}}};return manager}class AdbStream extends EventEmitter{constructor(localId,remoteId,socket,manager){super();this.localId=localId;this.remoteId=remoteId;this.socket=socket;this.manager=manager;this.ready=false;this.closed=false}handleMessage(command,arg0,arg1,data){switch(command){case MSG_OKAY:if(!this.ready){this.remoteId=arg0;this.ready=true;this.emit("ready")}else{this.emit("ack")}break;case MSG_WRTE:this.emit("data",data);sendMessage(this.socket,MSG_OKAY,this.localId,this.remoteId);break;case MSG_CLSE:this.closed=true;this.emit("close");break}}async write(data){if(this.closed||!this.ready){throw new Error("Stream not ready or closed")}const buffer=Buffer.isBuffer(data)?data:Buffer.from(data);await sendMessage(this.socket,MSG_WRTE,this.remoteId,this.localId,buffer)}close(){if(!this.closed){this.closed=true;sendMessage(this.socket,MSG_CLSE,this.remoteId,this.localId);this.emit("close")}}}async function sendMessage(socket,command,arg0,arg1,data=Buffer.alloc(0)){const header=Buffer.alloc(24);let offset=0;header.writeUInt32LE(command,offset);offset+=4;header.writeUInt32LE(arg0,offset);offset+=4;header.writeUInt32LE(arg1,offset);offset+=4;header.writeUInt32LE(data.length,offset);offset+=4;header.writeUInt32LE(checksum(data),offset);offset+=4;header.writeUInt32LE((command^4294967295)>>>0,offset);const commandName=getCommandName(command);if(self.config.get("debug")){self.log.debug(`${self.config.get("debugArrowSent")} ${commandName} arg0:${arg0} arg1:${arg1} len:${data.length}`)}socket.write(header);if(data.length>0){socket.write(data)}}function checksum(data){let sum=0;for(let i=0;i<data.length;i++){sum+=data[i]}return sum&4294967295}export{create};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ function parseProperties(propOutput){const props={};const lines=propOutput.split("\n");for(const line of lines){const match=line.match(/^\[([^\]]+)\]:\s*\[([^\]]*)\]$/);if(match){const[,key,value]=match;props[key]=value}}return props}function parseListing(lsOutput){const entries=[];const lines=lsOutput.split("\n").filter(line=>line.trim());const startIdx=lines[0]?.startsWith("total")?1:0;for(let i=startIdx;i<lines.length;i++){const line=lines[i];const match=line.match(/^([drwx-]+)\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(.+?)\s+(.+)$/);if(match){const[,permissions,links,owner,group,size,dateTime,name]=match;entries.push({name,permissions,links:parseInt(links),owner,group,size:parseInt(size),dateTime,isDirectory:permissions.startsWith("d"),isFile:permissions.startsWith("-"),isSymlink:permissions.startsWith("l")})}}return entries}function parseBattery(batteryOutput){const battery={};const lines=batteryOutput.split("\n");for(const line of lines){const match=line.match(/^\s*([^:]+):\s*(.+)$/);if(match){let[,key,value]=match;key=key.trim();value=value.trim();if(/^\d+$/.test(value)){value=parseInt(value)}battery[key]=value}}return battery}function formatBytes(bytes,decimals=2){if(bytes===0)return"0 Bytes";const k=1024;const dm=decimals<0?0:decimals;const sizes=["Bytes","KB","MB","GB","TB"];const i=Math.floor(Math.log(bytes)/Math.log(k));return parseFloat((bytes/Math.pow(k,i)).toFixed(dm))+" "+sizes[i]}function delay(ms){return new Promise(resolve=>setTimeout(resolve,ms))}async function retry(fn,options={}){const{baseDelay=1e3,maxDelay=1e4,shouldRetry=()=>true}=options;let{maxRetries=3}=options;if(maxRetries<0){console.warn(`retry: maxRetries must be >= 0, got ${maxRetries} - clamping to 0`);maxRetries=0}let lastError;for(let attempt=0;attempt<=maxRetries;attempt++){try{return await fn()}catch(error){lastError=error;if(attempt===maxRetries||!shouldRetry(error)){throw error}const delayMs=Math.min(baseDelay*Math.pow(2,attempt),maxDelay);await delay(delayMs)}}throw lastError}function isValidIP(ip){const parts=ip.split(".");if(parts.length!==4)return false;return parts.every(part=>{const num=parseInt(part,10);return num>=0&&num<=255&&part===num.toString()})}function isValidPort(port){return Number.isInteger(port)&&port>=1&&port<=65535}function parseHostPort(hostPort,defaultPort=5555){const colonIndex=hostPort.lastIndexOf(":");if(colonIndex===-1){return{host:hostPort,port:defaultPort}}const host=hostPort.substring(0,colonIndex);const portStr=hostPort.substring(colonIndex+1);const port=parseInt(portStr,10);if(!isValidPort(port)){throw new Error(`Invalid port: ${portStr}`)}return{host,port}}function escapeShell(str){return str.replace(/[\\$`"]/g,"\\$&")}function timeout(ms,message="Operation timed out"){return new Promise((_,reject)=>{setTimeout(()=>reject(new Error(message)),ms)})}function withTimeout(promise,ms,message){return Promise.race([promise,timeout(ms,message)])}export{delay,escapeShell,formatBytes,isValidIP,isValidPort,parseBattery,parseHostPort,parseListing,parseProperties,retry,timeout,withTimeout};
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import slothlet from"@cldmv/slothlet";import path from"node:path";import{fileURLToPath}from"node:url";const __filename=fileURLToPath(import.meta.url);const __dirname=path.dirname(__filename);async function createDroidSock(options={}){const{mode="eager",context={},config={},...slothletOptions}=options;const api=await slothlet({base:path.join(__dirname,"api"),mode,runtime:"async",context:{...context},debug:false,sanitize:{lowerFirst:false,rules:{leave:["ADB","TCP","USB","Auth","Sync"],upper:["adb*","tcp*","usb*"]}},...slothletOptions});if(Object.keys(config).length>0){api.config.init(config)}return api}export{createDroidSock as default};
package/index.cjs CHANGED
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @Project: @cldmv/droidsock
3
3
  * @Filename: /index.cjs
4
- * @Date: 2025-11-21 14:04:10 -08:00
5
- * @Author: Nate Hyson <CLDMV>
4
+ * @Date: 2025-11-21T15:41:06-08:00 (1763768466)
5
+ * @Author: Shinrai <CLDMV>
6
6
  * @Email: <Shinrai@users.noreply.github.com>
7
7
  * -----
8
- * @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
9
- * @Last modified time: 2025-11-21 16:22:18 -08:00
8
+ * @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
9
+ * @Last modified time: 2026-08-30 21:00:34 -07:00 (1788148834)
10
10
  * -----
11
- * @Copyright: Copyright (c) 2013-2025 Catalyzed Motivation Inc. All rights reserved.
11
+ * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
12
12
  */
13
13
 
14
14
  /**
@@ -23,15 +23,13 @@
23
23
  const { createRequire } = require("module");
24
24
  const requireESM = createRequire(__filename);
25
25
 
26
- const { default: createDroidSock, connect, listDevices } = requireESM("./index.mjs");
26
+ const { default: droidsock } = requireESM("./index.mjs");
27
27
 
28
- // Export main function
29
- module.exports = createDroidSock; // Default export
30
- module.exports.createDroidSock = createDroidSock;
31
- module.exports.connect = connect;
32
- module.exports.listDevices = listDevices;
28
+ // Export main function - the quick path, also callable with options
29
+ module.exports = droidsock; // Default export
30
+ module.exports.createDroidSock = droidsock;
33
31
 
34
32
  // Common DroidSock aliases
35
- module.exports.DroidSock = createDroidSock;
36
- module.exports.ADB = createDroidSock;
37
- module.exports.AndroidDebugBridge = createDroidSock;
33
+ module.exports.DroidSock = droidsock;
34
+ module.exports.ADB = droidsock;
35
+ module.exports.AndroidDebugBridge = droidsock;
package/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @Project: @cldmv/droidsock
3
3
  * @Filename: /index.mjs
4
- * @Date: 2025-11-21 14:04:10 -08:00
5
- * @Author: Nate Hyson <CLDMV>
4
+ * @Date: 2025-11-21T15:41:06-08:00 (1763768466)
5
+ * @Author: Shinrai <CLDMV>
6
6
  * @Email: <Shinrai@users.noreply.github.com>
7
7
  * -----
8
- * @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
9
- * @Last modified time: 2025-11-21 14:47:33 -08:00 (1763765253)
8
+ * @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
9
+ * @Last modified time: 2026-08-30 21:00:34 -07:00 (1788148834)
10
10
  * -----
11
- * @Copyright: Copyright (c) 2013-2025 Catalyzed Motivation Inc. All rights reserved.
11
+ * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
12
12
  */
13
13
 
14
14
  // Development environment check (must happen before droidsock imports)
@@ -21,37 +21,22 @@
21
21
  })();
22
22
 
23
23
  /**
24
- * Creates a DroidSock instance for ADB communication
24
+ * Creates a DroidSock instance for ADB communication. This is the default export - the
25
+ * quick path - and also available under the explicit name `createDroidSock` for callers
26
+ * who prefer it.
25
27
  * @param {object} [options={}] - Configuration options
26
28
  * @returns {Promise<object>} DroidSock instance
27
29
  */
28
- export default async function createDroidSock(options = {}) {
30
+ async function droidsock(options = {}) {
29
31
  // Dynamic import after environment check
30
32
  const mod = await import("@cldmv/droidsock/main");
31
- const createDroidSockImpl = mod.default;
32
- return await createDroidSockImpl(options);
33
+ return await mod.default(options);
33
34
  }
34
35
 
35
- /**
36
- * Connect to a device
37
- * @param {string} deviceId - Device ID to connect to
38
- * @returns {Promise<object>} Connected device instance
39
- */
40
- export async function connect(deviceId) {
41
- const mod = await import("@cldmv/droidsock/main");
42
- return mod.connect(deviceId);
43
- }
44
-
45
- /**
46
- * List available devices
47
- * @returns {Promise<Array>} List of available devices
48
- */
49
- export async function listDevices() {
50
- const mod = await import("@cldmv/droidsock/main");
51
- return mod.listDevices();
52
- }
36
+ export default droidsock;
37
+ export { droidsock as createDroidSock };
53
38
 
54
39
  // Named export aliases
55
- export { createDroidSock as DroidSock };
56
- export { createDroidSock as ADB };
57
- export { createDroidSock as AndroidDebugBridge };
40
+ export { droidsock as DroidSock };
41
+ export { droidsock as ADB };
42
+ export { droidsock as AndroidDebugBridge };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cldmv/droidsock",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Complete Node.js implementation of the Android Debug Bridge (ADB) protocol",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.mjs",
@@ -16,7 +16,7 @@
16
16
  "import": "./devcheck.mjs"
17
17
  },
18
18
  "./main": {
19
- "development": {
19
+ "droidsock-dev": {
20
20
  "types": "./types/src/droidsock.d.mts",
21
21
  "import": "./src/droidsock.mjs"
22
22
  },
@@ -29,15 +29,21 @@
29
29
  "node": ">=18.12"
30
30
  },
31
31
  "scripts": {
32
- "test": "vitest run --config .configs/vitest.config.mjs",
33
- "test:run": "vitest run",
34
- "test:coverage": "vitest run --coverage",
35
- "test:types": "tsc --noEmit --project .configs/tsconfig.dts.jsonc",
36
- "lint": "eslint --config .configs/eslint.config.mjs .",
37
32
  "build": "node build.mjs",
38
33
  "build:types": "tsc --project .configs/tsconfig.dts.jsonc",
39
- "build:ci": "npm run build:types && npm run test:types && npm run build",
40
- "precommit": "npm run build:types && npm run test:types && npm run lint && npm run test"
34
+ "build:ci": "npm run build && npm run build:types && npm run test:types",
35
+ "test": "node tests/run-vitest.mjs",
36
+ "test:watch": "vitest --config .configs/vitest.config.mjs",
37
+ "test:types": "tsc --noEmit --project .configs/tsconfig.dts.jsonc",
38
+ "coverage": "node tests/run-vitest.mjs --coverage-quiet",
39
+ "ci:coverage": "npm run coverage",
40
+ "lint": "eslint --config .configs/eslint.config.mjs .",
41
+ "lint:fix": "eslint --config .configs/eslint.config.mjs . --fix",
42
+ "format": "prettier --config .configs/.prettierrc --write .",
43
+ "format:check": "prettier --config .configs/.prettierrc --check .",
44
+ "fix:headers": "node tools/fix-headers.mjs",
45
+ "prepare": "node -e \"import('./.githooks/install.mjs').catch(()=>{})\"",
46
+ "precommit": "npm run build && npm run build:types && npm run test:types && npm run lint && npm run format:check && npm run test"
41
47
  },
42
48
  "keywords": [
43
49
  "adb",
@@ -98,21 +104,25 @@
98
104
  ],
99
105
  "sideEffects": false,
100
106
  "devDependencies": {
101
- "@eslint/css": "^0.14.1",
102
- "@eslint/js": "^9.39.1",
103
- "@eslint/json": "^0.14.0",
104
- "@eslint/markdown": "^7.5.1",
105
- "@html-eslint/eslint-plugin": "^0.48.0",
106
- "@html-eslint/parser": "^0.48.0",
107
- "@types/node": "^20.0.0",
108
- "@vitest/ui": "^4.0.8",
109
- "eslint": "^9.39.1",
110
- "globals": "^16.5.0",
111
- "prettier": "^3.0.0",
112
- "typescript": "^5.0.0",
113
- "vitest": "^4.0.8"
107
+ "@cldmv/eslint-plugin-jsonv": "^1.0.3",
108
+ "@cldmv/fix-headers": "^1.3.10",
109
+ "@cldmv/jsonv": "^1.0.2",
110
+ "@cldmv/prettier-plugin-jsonv": "^1.0.1",
111
+ "@cldmv/vitest-runner": "^1.2.0",
112
+ "@eslint/css": "^1.4.0",
113
+ "@eslint/js": "^10.0.1",
114
+ "@eslint/json": "^2.0.1",
115
+ "@eslint/markdown": "^8.0.3",
116
+ "@types/node": "^26.4.0",
117
+ "@vitest/coverage-v8": "^4.1.11",
118
+ "esbuild": "^0.28.2",
119
+ "eslint": "^10.9.1",
120
+ "globals": "^17.11.0",
121
+ "prettier": "^3.9.6",
122
+ "typescript": "^6.0.3",
123
+ "vitest": "^4.1.11"
114
124
  },
115
125
  "dependencies": {
116
- "@cldmv/slothlet": "^2.6.1"
126
+ "@cldmv/slothlet": "^3.15.0"
117
127
  }
118
128
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=devcheck.d.mts.map
@@ -0,0 +1,14 @@
1
+ export function formatAdbPublicKey(publicKey: any): string;
2
+ export function generateKeys(keySize?: number, saveDir?: null): {
3
+ privateKey: any;
4
+ publicKey: any;
5
+ adbPublicKey: string;
6
+ };
7
+ export function getKeys(keyDir: any): {
8
+ privateKey: any;
9
+ publicKey: any;
10
+ adbPublicKey: any;
11
+ };
12
+ export function sign(token: any, privateKey: any): any;
13
+ export function validateAuth(token: any, privateKey: any): boolean;
14
+ //# sourceMappingURL=auth.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.mts","sourceRoot":"","sources":["../../../dist/api/auth.mjs"],"names":[],"mappings":"AAgBqmH,2DAAkyC;AAApnI;;;;EAA2kB;AAAxsC;;;;EAA6nB;AAA2kB,uDAAw6C;AAA6lB,mEAAkQ"}
@@ -0,0 +1,52 @@
1
+ export function all(): any;
2
+ export function get(key: any, defaultValue?: undefined): any;
3
+ export function getApi(): {
4
+ init: typeof init;
5
+ get: typeof get;
6
+ set: typeof set;
7
+ merge: typeof merge;
8
+ reset: typeof reset;
9
+ all: typeof all;
10
+ };
11
+ export function getDefaults(): {
12
+ host: string;
13
+ port: number;
14
+ timeout: number;
15
+ retryAttempts: number;
16
+ retryDelay: number;
17
+ keyDir: null;
18
+ autoGenerateKeys: boolean;
19
+ keySize: number;
20
+ debug: boolean;
21
+ verbose: boolean;
22
+ silent: boolean;
23
+ debugArrowSent: string;
24
+ debugArrowReceived: string;
25
+ streamTimeout: number;
26
+ maxStreams: number;
27
+ shellTimeout: number;
28
+ shellEncoding: string;
29
+ fileTimeout: number;
30
+ chunkSize: number;
31
+ emitEvents: boolean;
32
+ eventPrefix: string;
33
+ bufferSize: number;
34
+ keepAlive: boolean;
35
+ keepAliveInterval: number;
36
+ };
37
+ export function init(options?: {}): {
38
+ init: typeof init;
39
+ get: typeof get;
40
+ set: typeof set;
41
+ merge: typeof merge;
42
+ reset: typeof reset;
43
+ all: typeof all;
44
+ };
45
+ export function merge(options: any): void;
46
+ export function reset(): void;
47
+ export function set(key: any, value: any): void;
48
+ export function validateConfig(config: any): {
49
+ valid: boolean;
50
+ errors: string[];
51
+ };
52
+ //# sourceMappingURL=config.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.mts","sourceRoot":"","sources":["../../../dist/api/config.mjs"],"names":[],"mappings":"AAgBw+C,2BAA6E;AAA5gC,6DAA8O;AAA8xB;;;;;;;EAAuD;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;EAAiD;AAAhuC;;;;;;;EAA4G;AAAie,0CAA0a;AAAA,8BAAoD;AAAjtB,gDAAmP;AAAmpB;;;EAAswB"}
@@ -0,0 +1,2 @@
1
+ export function create(options: any): Promise<any>;
2
+ //# sourceMappingURL=connection.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.mts","sourceRoot":"","sources":["../../../dist/api/connection.mjs"],"names":[],"mappings":"AAgBiN,mDAAg/F"}
@@ -0,0 +1,5 @@
1
+ export function connect(host: any, port?: number, options?: {}): Promise<any>;
2
+ export function disconnect(host: any, port?: number): boolean;
3
+ export function disconnectAll(): number;
4
+ export function list(): any[];
5
+ //# sourceMappingURL=device.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.mts","sourceRoot":"","sources":["../../../dist/api/device.mjs"],"names":[],"mappings":"AAgBoE,8EAA4wH;AAA6F,8DAAoK;AAAA,wCAA4I;AAA7Y,8BAA6F"}
@@ -0,0 +1,12 @@
1
+ export function chmod(socket: any, streamManager: any, remotePath: any, mode: any, recursive?: boolean): Promise<any>;
2
+ export function copy(socket: any, streamManager: any, sourcePath: any, destPath: any, recursive?: boolean): Promise<any>;
3
+ export function diskUsage(socket: any, streamManager: any, path?: string): Promise<any>;
4
+ export function find(socket: any, streamManager: any, path: any, pattern: any, options?: {}): Promise<any>;
5
+ export function list(___socket: any, ___streamManager: any, ___remotePath: any): Promise<void>;
6
+ export function mkdir(socket: any, streamManager: any, remotePath: any, mode?: number): Promise<any>;
7
+ export function move(socket: any, streamManager: any, sourcePath: any, destPath: any): Promise<any>;
8
+ export function pull(___socket: any, ___streamManager: any, ___remotePath: any, ___localPath: any, ___options?: {}): Promise<void>;
9
+ export function push(___socket: any, ___streamManager: any, ___localPath: any, ___remotePath: any, ___options?: {}): Promise<void>;
10
+ export function remove(socket: any, streamManager: any, remotePath: any, recursive?: boolean): Promise<any>;
11
+ export function stat(___socket: any, ___streamManager: any, ___remotePath: any): Promise<void>;
12
+ //# sourceMappingURL=files.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.mts","sourceRoot":"","sources":["../../../dist/api/files.mjs"],"names":[],"mappings":"AAgB86C,sHAAwO;AAA1c,yHAAkO;AAAwO,wFAAsJ;AAAA,2GAA8S;AAAhvD,+FAA6I;AAAqI,qGAAmN;AAAgN,oGAA6K;AAAlgC,mIAAgK;AAAhU,mIAAgK;AAAqoB,4GAAgN;AAAxiB,+FAAqI"}
@@ -0,0 +1,21 @@
1
+ export function child(context: any): {
2
+ debug: (...args: any[]) => void;
3
+ verbose: (...args: any[]) => void;
4
+ info: (...args: any[]) => void;
5
+ warn: (...args: any[]) => void;
6
+ error: (...args: any[]) => void;
7
+ };
8
+ export function debug(...args: any[]): void;
9
+ export function error(...args: any[]): void;
10
+ export function getApi(): {
11
+ debug: typeof debug;
12
+ verbose: typeof verbose;
13
+ info: typeof info;
14
+ warn: typeof warn;
15
+ error: typeof error;
16
+ child: typeof child;
17
+ };
18
+ export function info(...args: any[]): void;
19
+ export function verbose(...args: any[]): void;
20
+ export function warn(...args: any[]): void;
21
+ //# sourceMappingURL=log.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.mts","sourceRoot":"","sources":["../../../dist/api/log.mjs"],"names":[],"mappings":"AAgBkxB;;;;;;EAAmoB;AAAlwC,4CAA+I;AAAmY,4CAA6G;AAAmoB;;;;;;;EAA8D;AAAjgC,2CAAyG;AAAzR,8CAAgL;AAAyG,2CAA0G"}
@@ -0,0 +1,29 @@
1
+ export namespace commands {
2
+ function ls(socket: any, streamManager: any, path?: string): Promise<any>;
3
+ function pwd(socket: any, streamManager: any): Promise<any>;
4
+ function getprop(socket: any, streamManager: any, prop?: null): Promise<any>;
5
+ function getModel(socket: any, streamManager: any): Promise<any>;
6
+ function getAndroidVersion(socket: any, streamManager: any): Promise<any>;
7
+ function getBattery(socket: any, streamManager: any): Promise<any>;
8
+ function screenshot(socket: any, streamManager: any, filename?: string): Promise<any>;
9
+ function logcat(socket: any, streamManager: any, options?: {}): {
10
+ stop(): void;
11
+ };
12
+ function top(socket: any, streamManager: any, options?: {}): {
13
+ stop(): void;
14
+ };
15
+ function keypress(socket: any, streamManager: any, key: any): Promise<any>;
16
+ function launchApp(socket: any, streamManager: any, packageName: any, activity?: string): Promise<any>;
17
+ function killApp(socket: any, streamManager: any, packageName: any): Promise<any>;
18
+ function installApk(socket: any, streamManager: any, apkPath: any, flags?: any[]): Promise<any>;
19
+ function uninstallApp(socket: any, streamManager: any, packageName: any): Promise<any>;
20
+ }
21
+ export function execute(socket: any, streamManager: any, command: any, options?: {}): Promise<any>;
22
+ export function startInteractive(socket: any, streamManager: any, command: any, options?: {}): {
23
+ sendInput(input: any): Promise<void>;
24
+ stop(): void;
25
+ };
26
+ export function startStreaming(socket: any, streamManager: any, command: any, options?: {}): {
27
+ stop(): void;
28
+ };
29
+ //# sourceMappingURL=shell.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.mts","sourceRoot":"","sources":["../../../dist/api/shell.mjs"],"names":[],"mappings":";IAgBoyH,0EAAqG;IAAK,4DAA+E;IAAS,6EAAoI;IAAU,iEAAoG;IAAmB,0EAA4G;IAAY,mEAA2F;IAAY,sFAAwI;IAAQ;;MAAiG;IAAK;;MAAoG;IAAU,2EAAqG;IAAW,uGAAyL;IAAS,kFAAoH;IAAY,gGAAsL;IAAc,uFAAmH;;AAA7+K,mGAA+vF;AAAod;;;EAAohB;AAAx+B;;EAAod"}
@@ -0,0 +1,9 @@
1
+ export function create(socket: any): {
2
+ socket: any;
3
+ streams: Map<any, any>;
4
+ nextStreamId: number;
5
+ openStream(destination: any): Promise<any>;
6
+ handlePacket(data: any): void;
7
+ closeStream(streamId: any): void;
8
+ };
9
+ //# sourceMappingURL=stream.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.mts","sourceRoot":"","sources":["../../../dist/api/stream.mjs"],"names":[],"mappings":"AAgB8Y;;;;;;;EAAs0C"}
@@ -0,0 +1,27 @@
1
+ export function delay(ms: any): Promise<any>;
2
+ export function escapeShell(str: any): any;
3
+ export function formatBytes(bytes: any, decimals?: number): string;
4
+ export function isValidIP(ip: any): any;
5
+ export function isValidPort(port: any): boolean;
6
+ export function parseBattery(batteryOutput: any): {};
7
+ export function parseHostPort(hostPort: any, defaultPort?: number): {
8
+ host: any;
9
+ port: number;
10
+ };
11
+ export function parseListing(lsOutput: any): {
12
+ name: any;
13
+ permissions: any;
14
+ links: number;
15
+ owner: any;
16
+ group: any;
17
+ size: number;
18
+ dateTime: any;
19
+ isDirectory: any;
20
+ isFile: any;
21
+ isSymlink: any;
22
+ }[];
23
+ export function parseProperties(propOutput: any): {};
24
+ export function retry(fn: any, options?: {}): Promise<any>;
25
+ export function timeout(ms: any, message?: string): Promise<any>;
26
+ export function withTimeout(promise: any, ms: any, message: any): Promise<any>;
27
+ //# sourceMappingURL=utils.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../../../dist/api/utils.mjs"],"names":[],"mappings":"AAgBq4C,6CAAuE;AAA8mC,2CAAgE;AAAjgD,mEAA4Q;AAAqkB,wCAAyL;AAAA,gDAA+E;AAAh5C,qDAAuT;AAAylC;;;EAAwW;AAAh1E;;;;;;;;;;;IAAwlB;AAAl0B,qDAA0O;AAAkuC,2DAA8f;AAAgrB,iEAAmI;AAAA,+EAA4F"}
@@ -0,0 +1,3 @@
1
+ export { createDroidSock as default };
2
+ declare function createDroidSock(options?: {}): Promise<any>;
3
+ //# sourceMappingURL=droidsock.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"droidsock.d.mts","sourceRoot":"","sources":["../../dist/droidsock.mjs"],"names":[],"mappings":";AAgB+L,6DAAia"}
@@ -0,0 +1,11 @@
1
+ export default droidsock;
2
+ /**
3
+ * Creates a DroidSock instance for ADB communication. This is the default export - the
4
+ * quick path - and also available under the explicit name `createDroidSock` for callers
5
+ * who prefer it.
6
+ * @param {object} [options={}] - Configuration options
7
+ * @returns {Promise<object>} DroidSock instance
8
+ */
9
+ declare function droidsock(options?: object): Promise<object>;
10
+ export { droidsock as createDroidSock, droidsock as DroidSock, droidsock as ADB, droidsock as AndroidDebugBridge };
11
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mjs"],"names":[],"mappings":";AAsBA;;;;;;GAMG;AACH,qCAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAM3B"}