@beeos-ai/device-mcp-server 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/backends/android-adb-runner.d.ts +32 -0
- package/dist/backends/android-adb-runner.js +15 -0
- package/dist/backends/android-adb-runner.js.map +1 -0
- package/dist/backends/android-adb.d.ts +153 -0
- package/dist/backends/android-adb.js +723 -0
- package/dist/backends/android-adb.js.map +1 -0
- package/dist/backends/base.d.ts +150 -0
- package/dist/backends/base.js +116 -0
- package/dist/backends/base.js.map +1 -0
- package/dist/backends/desktop.d.ts +62 -0
- package/dist/backends/desktop.js +176 -0
- package/dist/backends/desktop.js.map +1 -0
- package/dist/backends/index.d.ts +63 -0
- package/dist/backends/index.js +105 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/linux.d.ts +69 -0
- package/dist/backends/linux.js +230 -0
- package/dist/backends/linux.js.map +1 -0
- package/dist/backends/mac.d.ts +154 -0
- package/dist/backends/mac.js +881 -0
- package/dist/backends/mac.js.map +1 -0
- package/dist/backends/stubs/ios.d.ts +17 -0
- package/dist/backends/stubs/ios.js +32 -0
- package/dist/backends/stubs/ios.js.map +1 -0
- package/dist/backends/stubs/macos.d.ts +13 -0
- package/dist/backends/stubs/macos.js +27 -0
- package/dist/backends/stubs/macos.js.map +1 -0
- package/dist/backends/stubs/windows.d.ts +69 -0
- package/dist/backends/stubs/windows.js +191 -0
- package/dist/backends/stubs/windows.js.map +1 -0
- package/dist/cli.d.ts +37 -0
- package/dist/cli.js +177 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/server/action-mapping.d.ts +21 -0
- package/dist/server/action-mapping.js +153 -0
- package/dist/server/action-mapping.js.map +1 -0
- package/dist/server/app.d.ts +23 -0
- package/dist/server/app.js +157 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/tool-registry.d.ts +50 -0
- package/dist/server/tool-registry.js +504 -0
- package/dist/server/tool-registry.js.map +1 -0
- package/dist/util/adb-files.d.ts +92 -0
- package/dist/util/adb-files.js +221 -0
- package/dist/util/adb-files.js.map +1 -0
- package/dist/util/adb-shell.d.ts +80 -0
- package/dist/util/adb-shell.js +102 -0
- package/dist/util/adb-shell.js.map +1 -0
- package/dist/util/android-apps.d.ts +10 -0
- package/dist/util/android-apps.js +103 -0
- package/dist/util/android-apps.js.map +1 -0
- package/dist/util/image-dim.d.ts +27 -0
- package/dist/util/image-dim.js +37 -0
- package/dist/util/image-dim.js.map +1 -0
- package/dist/util/ui-xml.d.ts +20 -0
- package/dist/util/ui-xml.js +184 -0
- package/dist/util/ui-xml.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adb-files — file-IO and listing primitives over the native `adb`
|
|
3
|
+
* `pull` / `push` / `shell ls` protocols.
|
|
4
|
+
*
|
|
5
|
+
* # Why this module exists
|
|
6
|
+
*
|
|
7
|
+
* Earlier versions of the Android backend did file IO with two ad-hoc
|
|
8
|
+
* tricks:
|
|
9
|
+
*
|
|
10
|
+
* - `fileRead` → `adb exec-out cat <path>` and decoded the bytes from
|
|
11
|
+
* stdout. This silently swallowed device-side `cat` errors because
|
|
12
|
+
* `adb exec-out` does NOT propagate the remote command's exit code
|
|
13
|
+
* back to the host adb client (the host always exits 0 when the
|
|
14
|
+
* channel closes cleanly), so reading a non-existent path returned
|
|
15
|
+
* `cat`'s stderr text base64-encoded as if it were file content.
|
|
16
|
+
*
|
|
17
|
+
* - `fileWrite` → `adb shell sh -c "echo <b64> | base64 -d > <path>"`.
|
|
18
|
+
* Because `adb shell` joins argv with spaces and re-evaluates through
|
|
19
|
+
* `sh -c`, the shell saw `sh -c sh -c echo <b64> | base64 -d > path`,
|
|
20
|
+
* which dropped the pipeline body, ran `sh -c echo` with no args, and
|
|
21
|
+
* left an empty file on the device. The bug went undetected because
|
|
22
|
+
* the host-side `adb` still returned exit 0.
|
|
23
|
+
*
|
|
24
|
+
* The right tools for this job are `adb pull` and `adb push`: they speak
|
|
25
|
+
* the file-sync protocol designed for binary transfer, the host-side
|
|
26
|
+
* `adb` exits with a non-zero code when the device-side path is missing
|
|
27
|
+
* or unwritable, and they side-step the shell tokeniser entirely. This
|
|
28
|
+
* module wraps those primitives in async-friendly helpers and routes
|
|
29
|
+
* every error through `wrapAdbFailure` so callers see the familiar
|
|
30
|
+
* `DeviceError` shape.
|
|
31
|
+
*
|
|
32
|
+
* `adbListDir` exists in this module rather than in the backend so the
|
|
33
|
+
* "use `ls -laL` to dereference symlinks" rule has a single home. The
|
|
34
|
+
* raw `ls -la` form does not deref symlinks (so listing `/sdcard` returns
|
|
35
|
+
* the symlink itself instead of the contents of `/storage/self/primary`,
|
|
36
|
+
* which is almost never what callers want).
|
|
37
|
+
*/
|
|
38
|
+
import { mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
39
|
+
import * as os from "node:os";
|
|
40
|
+
import * as path from "node:path";
|
|
41
|
+
import { DeviceError } from "@beeos-ai/device-common";
|
|
42
|
+
import { sh } from "./adb-shell.js";
|
|
43
|
+
const DEFAULT_PULL_CAP_BYTES = 64 * 1024 * 1024;
|
|
44
|
+
/**
|
|
45
|
+
* Prepend `["-s", serial]` when a serial is set. Mirrors the private
|
|
46
|
+
* `adbArgs()` helper in the backend so the helpers here can build their
|
|
47
|
+
* own argv without leaking the backend's internal state.
|
|
48
|
+
*/
|
|
49
|
+
export function withSerial(serial, args) {
|
|
50
|
+
return serial ? ["-s", serial, ...args] : [...args];
|
|
51
|
+
}
|
|
52
|
+
/* ----------------------------------------------------------------------- */
|
|
53
|
+
/* Pull / push */
|
|
54
|
+
/* ----------------------------------------------------------------------- */
|
|
55
|
+
/**
|
|
56
|
+
* `adb pull <remote> <hostTmp>` followed by an in-memory read of the
|
|
57
|
+
* temp file. Throws `DeviceError(file_not_found)` when the device path
|
|
58
|
+
* does not exist (host-side adb exits with code 1 and prints
|
|
59
|
+
* `adb: error: failed to stat remote object ... No such file or directory`),
|
|
60
|
+
* and the generic `adb_failed` shape for everything else.
|
|
61
|
+
*/
|
|
62
|
+
export async function adbPull(runner, serial, remote, opts) {
|
|
63
|
+
const tmpdir = opts.tmpdir?.() ?? os.tmpdir();
|
|
64
|
+
const stagingDir = await mkdtemp(path.join(tmpdir, "beeos-adb-pull-"));
|
|
65
|
+
const local = path.join(stagingDir, "data.bin");
|
|
66
|
+
try {
|
|
67
|
+
await runner("adb", withSerial(serial, ["pull", remote, local]), {
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
timeoutMs: opts.timeoutMs,
|
|
70
|
+
maxBufferBytes: opts.maxBufferBytes ?? DEFAULT_PULL_CAP_BYTES,
|
|
71
|
+
});
|
|
72
|
+
return await readFile(local);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
throw wrapPullPushFailure("pull", remote, e, opts.timeoutMs);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
// Best-effort cleanup. Failure to clean up a tmp dir is never the
|
|
79
|
+
// caller's problem to handle.
|
|
80
|
+
await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `adb push <hostTmp> <remote>`. The buffer is written to a temp file
|
|
85
|
+
* first because `adb push` reads from disk; piping into stdin is not
|
|
86
|
+
* supported by the file-sync protocol.
|
|
87
|
+
*/
|
|
88
|
+
export async function adbPush(runner, serial, remote, content, opts) {
|
|
89
|
+
const tmpdir = opts.tmpdir?.() ?? os.tmpdir();
|
|
90
|
+
const stagingDir = await mkdtemp(path.join(tmpdir, "beeos-adb-push-"));
|
|
91
|
+
const local = path.join(stagingDir, "data.bin");
|
|
92
|
+
try {
|
|
93
|
+
await writeFile(local, content);
|
|
94
|
+
await runner("adb", withSerial(serial, ["push", local, remote]), {
|
|
95
|
+
encoding: "utf8",
|
|
96
|
+
timeoutMs: opts.timeoutMs,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
throw wrapPullPushFailure("push", remote, e, opts.timeoutMs);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
await unlink(local).catch(() => undefined);
|
|
104
|
+
await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/* ----------------------------------------------------------------------- */
|
|
108
|
+
/* List directory */
|
|
109
|
+
/* ----------------------------------------------------------------------- */
|
|
110
|
+
/**
|
|
111
|
+
* `ls -laL <path>` over `adb shell` (single-string command, properly
|
|
112
|
+
* quoted via `sh\`...\``). The `-L` flag dereferences symlinks so callers
|
|
113
|
+
* who pass `/sdcard` (a symlink to `/storage/self/primary` on stock AOSP)
|
|
114
|
+
* see the contents of the linked directory rather than a single
|
|
115
|
+
* `lrwxrwxrwx ... /sdcard -> /storage/self/primary` row.
|
|
116
|
+
*
|
|
117
|
+
* The runner contract is: throws on non-zero adb exit (the call site
|
|
118
|
+
* handles that with `wrapAdbFailure`); on success, parses the textual
|
|
119
|
+
* output via `parseLsOutput`.
|
|
120
|
+
*/
|
|
121
|
+
export async function adbListDir(runner, serial, pathArg, opts) {
|
|
122
|
+
const cmd = sh `ls -laL ${pathArg}`;
|
|
123
|
+
const { stdout } = await runner("adb", withSerial(serial, ["shell", cmd]), {
|
|
124
|
+
encoding: "utf8",
|
|
125
|
+
timeoutMs: opts.timeoutMs,
|
|
126
|
+
});
|
|
127
|
+
const text = typeof stdout === "string" ? stdout : stdout.toString("utf8");
|
|
128
|
+
return parseLsOutput(text);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Parse the output of `ls -la` / `ls -laL` from Android's toybox `ls`
|
|
132
|
+
* into structured entries. Tolerates header lines (`total 12`) and skips
|
|
133
|
+
* the canonical `.` / `..` entries.
|
|
134
|
+
*
|
|
135
|
+
* Entry layout (toybox): `perms links owner group size month day time name`.
|
|
136
|
+
* We index by whitespace-split position rather than parsing each column
|
|
137
|
+
* type-by-type because Android's `ls` output is stable across releases.
|
|
138
|
+
*
|
|
139
|
+
* Exported here (not in the backend) so the symlink-dereferencing rule
|
|
140
|
+
* lives next to the `ls -laL` invocation it depends on.
|
|
141
|
+
*/
|
|
142
|
+
export function parseLsOutput(out) {
|
|
143
|
+
const lines = out.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
144
|
+
const entries = [];
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
if (line.startsWith("total"))
|
|
147
|
+
continue;
|
|
148
|
+
const parts = line.split(/\s+/);
|
|
149
|
+
if (parts.length < 8)
|
|
150
|
+
continue;
|
|
151
|
+
const perms = parts[0];
|
|
152
|
+
const sizeStr = parts[4];
|
|
153
|
+
const name = parts.slice(7).join(" ");
|
|
154
|
+
if (!name || name === "." || name === "..")
|
|
155
|
+
continue;
|
|
156
|
+
entries.push({
|
|
157
|
+
name,
|
|
158
|
+
isDir: perms.startsWith("d"),
|
|
159
|
+
size: Number(sizeStr) || 0,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return entries;
|
|
163
|
+
}
|
|
164
|
+
/* ----------------------------------------------------------------------- */
|
|
165
|
+
/* Error normalisation */
|
|
166
|
+
/* ----------------------------------------------------------------------- */
|
|
167
|
+
/**
|
|
168
|
+
* Translate `adb pull`/`adb push` failures into the shared `DeviceError`
|
|
169
|
+
* shape. Mirrors the verb-aware logic in `wrapAdbFailure` (kept duplicated
|
|
170
|
+
* here on purpose — `adb-shell.ts` is meant to be dependency-free of the
|
|
171
|
+
* concrete backend module to avoid an import cycle).
|
|
172
|
+
*
|
|
173
|
+
* Subtypes:
|
|
174
|
+
* - `adb_timeout` — runner killed by SIGTERM after timeout
|
|
175
|
+
* - `adb_not_installed` — `ENOENT` / non-numeric exit code
|
|
176
|
+
* - `file_not_found` — pull/push exit !=0 with stderr matching
|
|
177
|
+
* the canonical "no such file" / "failed to
|
|
178
|
+
* stat" / "remote object does not exist"
|
|
179
|
+
* messages emitted by adb's file-sync code
|
|
180
|
+
* - `adb_failed` — anything else
|
|
181
|
+
*/
|
|
182
|
+
function wrapPullPushFailure(verb, remote, cause, timeoutMs) {
|
|
183
|
+
const err = cause;
|
|
184
|
+
if (err.killed && err.signal === "SIGTERM") {
|
|
185
|
+
return new DeviceError(`adb ${verb} timed out after ${timeoutMs}ms`, { subtype: "adb_timeout", retriable: true, cause: err });
|
|
186
|
+
}
|
|
187
|
+
if (typeof err.code === "string") {
|
|
188
|
+
return new DeviceError(`adb not installed (${err.code}): ${err.message}`, { subtype: "adb_not_installed", retriable: false, cause: err });
|
|
189
|
+
}
|
|
190
|
+
// Some shells / spawn shapes set `code === undefined` when the binary
|
|
191
|
+
// itself is missing.
|
|
192
|
+
if (err.code === undefined &&
|
|
193
|
+
/ENOENT|not found|spawn .*not found/i.test(err.message ?? "")) {
|
|
194
|
+
return new DeviceError(`adb not installed: ${err.message}`, {
|
|
195
|
+
subtype: "adb_not_installed",
|
|
196
|
+
retriable: false,
|
|
197
|
+
cause: err,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const stderr = err.stderr?.trim() ?? "";
|
|
201
|
+
const stdout = typeof err.stdout === "string"
|
|
202
|
+
? err.stdout
|
|
203
|
+
: Buffer.isBuffer(err.stdout)
|
|
204
|
+
? err.stdout.toString("utf8")
|
|
205
|
+
: "";
|
|
206
|
+
const blob = `${stderr}\n${stdout}`;
|
|
207
|
+
const isMissing = /no such file or directory/i.test(blob) ||
|
|
208
|
+
/failed to stat/i.test(blob) ||
|
|
209
|
+
/does not exist/i.test(blob) ||
|
|
210
|
+
/remote object .* does not exist/i.test(blob);
|
|
211
|
+
if (isMissing) {
|
|
212
|
+
return new DeviceError(`adb ${verb} failed: ${remote}: no such file or directory`, { subtype: "file_not_found", retriable: false, cause: err });
|
|
213
|
+
}
|
|
214
|
+
const summary = stderr || err.message || "unknown error";
|
|
215
|
+
return new DeviceError(`adb ${verb} failed: ${summary}`, {
|
|
216
|
+
subtype: "adb_failed",
|
|
217
|
+
retriable: false,
|
|
218
|
+
cause: err,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=adb-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb-files.js","sourceRoot":"","sources":["../../src/util/adb-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adb-shell — quoting + safe command-building primitives for the
|
|
3
|
+
* `adb shell` protocol.
|
|
4
|
+
*
|
|
5
|
+
* # Why this module exists
|
|
6
|
+
*
|
|
7
|
+
* The wire reality of `adb shell <argv...>` is treacherous: `adbd` on the
|
|
8
|
+
* device side joins all argv elements with spaces, then re-evaluates the
|
|
9
|
+
* resulting string through `/system/bin/sh -c`. Every shell metacharacter
|
|
10
|
+
* the host passed in (`$`, `` ` ``, `\`, `'`, `"`, `(`, `)`, `;`, `&`, `|`,
|
|
11
|
+
* `*`, `?`, `#`, whitespace, glob brackets, ...) is therefore re-tokenised
|
|
12
|
+
* and possibly variable-expanded on the device. Real-world fallout:
|
|
13
|
+
*
|
|
14
|
+
* - `am start -n com.app/.Activity$Sub` → `$Sub` expands to the empty
|
|
15
|
+
* string, the activity is silently dropped, the launcher opens instead.
|
|
16
|
+
* - `["shell", "sh", "-c", "echo b64 | base64 -d > path"]` → joined to
|
|
17
|
+
* `sh -c sh -c echo b64 | base64 -d > path`; the inner `sh -c echo`
|
|
18
|
+
* runs with no body, the pipeline drains nothing, the file ends up
|
|
19
|
+
* empty.
|
|
20
|
+
*
|
|
21
|
+
* The contract this module enforces:
|
|
22
|
+
*
|
|
23
|
+
* 1. Backend code MUST NOT call `adb` with `["shell", argv1, argv2, ...]`
|
|
24
|
+
* (more than two elements). The only allowed shape is
|
|
25
|
+
* `["shell", "<single command string>"]`. The `assertShellSingleString`
|
|
26
|
+
* helper makes this an executable invariant in tests.
|
|
27
|
+
* 2. The single command string MUST be built with `sh\`...\``
|
|
28
|
+
* (tagged-template) or `shellEscape`. Plain string concatenation of
|
|
29
|
+
* untrusted values into a shell command is forbidden.
|
|
30
|
+
*
|
|
31
|
+
* Both rules are static-friendly: `sh\`...\`` is a tagged template that
|
|
32
|
+
* the type system can spot, and `assertShellSingleString` runs in every
|
|
33
|
+
* unit test through the mock runner. The combination eliminates an entire
|
|
34
|
+
* class of latent injection / silent-corruption bugs (see CHANGELOG 0.2.3).
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* POSIX single-quote escape — the canonical way to embed an arbitrary
|
|
38
|
+
* string into a `/bin/sh` command line.
|
|
39
|
+
*
|
|
40
|
+
* shellEscape("a'b$c") → "'a'\\''b$c'"
|
|
41
|
+
*
|
|
42
|
+
* Single quotes disable every metacharacter inside the quoted span; the
|
|
43
|
+
* only character that needs splicing is `'` itself, replaced by the
|
|
44
|
+
* familiar `'\''` four-byte sequence (close-quote, escaped quote, open).
|
|
45
|
+
*/
|
|
46
|
+
export declare function shellEscape(s: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Tagged template — builds a shell command string with auto-escaped
|
|
49
|
+
* interpolated values. Numbers pass through verbatim (they cannot
|
|
50
|
+
* introduce metacharacters); strings always go through `shellEscape`.
|
|
51
|
+
*
|
|
52
|
+
* sh`am start -n ${component}`
|
|
53
|
+
* // ^ "com.app/.Activity$Sub"
|
|
54
|
+
* // → "am start -n 'com.app/.Activity$Sub'"
|
|
55
|
+
*
|
|
56
|
+
* sh`input swipe ${x1} ${y1} ${x2} ${y2} ${duration}`
|
|
57
|
+
* // → "input swipe 100 200 300 400 250"
|
|
58
|
+
*
|
|
59
|
+
* Use this for every command that has a dynamic piece. Static literal
|
|
60
|
+
* commands (e.g. `wm size`) can be passed as a plain string — but going
|
|
61
|
+
* through `sh\`...\`` is a no-op cost and keeps the call site uniform.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sh(parts: TemplateStringsArray, ...vals: (string | number)[]): string;
|
|
64
|
+
/**
|
|
65
|
+
* Test-only invariant — assert that an `adb` argv invocation that targets
|
|
66
|
+
* the `shell` verb uses the single-string pattern (exactly one argument
|
|
67
|
+
* after `"shell"`, optionally preceded by `["-s", serial]`).
|
|
68
|
+
*
|
|
69
|
+
* Throws an `Error` with a descriptive message when the invariant is
|
|
70
|
+
* violated. Production code is expected to satisfy it by construction;
|
|
71
|
+
* the helper exists so tests can wire it through their mock runners and
|
|
72
|
+
* lock the contract from the outside.
|
|
73
|
+
*
|
|
74
|
+
* Examples:
|
|
75
|
+
* assertShellSingleString(["shell", "wm size"]) // ok
|
|
76
|
+
* assertShellSingleString(["-s", "abc", "shell", "wm size"]) // ok
|
|
77
|
+
* assertShellSingleString(["shell", "am", "start"]) // throws
|
|
78
|
+
* assertShellSingleString(["pull", "/x", "/y"]) // ok (not shell)
|
|
79
|
+
*/
|
|
80
|
+
export declare function assertShellSingleString(args: readonly string[]): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adb-shell — quoting + safe command-building primitives for the
|
|
3
|
+
* `adb shell` protocol.
|
|
4
|
+
*
|
|
5
|
+
* # Why this module exists
|
|
6
|
+
*
|
|
7
|
+
* The wire reality of `adb shell <argv...>` is treacherous: `adbd` on the
|
|
8
|
+
* device side joins all argv elements with spaces, then re-evaluates the
|
|
9
|
+
* resulting string through `/system/bin/sh -c`. Every shell metacharacter
|
|
10
|
+
* the host passed in (`$`, `` ` ``, `\`, `'`, `"`, `(`, `)`, `;`, `&`, `|`,
|
|
11
|
+
* `*`, `?`, `#`, whitespace, glob brackets, ...) is therefore re-tokenised
|
|
12
|
+
* and possibly variable-expanded on the device. Real-world fallout:
|
|
13
|
+
*
|
|
14
|
+
* - `am start -n com.app/.Activity$Sub` → `$Sub` expands to the empty
|
|
15
|
+
* string, the activity is silently dropped, the launcher opens instead.
|
|
16
|
+
* - `["shell", "sh", "-c", "echo b64 | base64 -d > path"]` → joined to
|
|
17
|
+
* `sh -c sh -c echo b64 | base64 -d > path`; the inner `sh -c echo`
|
|
18
|
+
* runs with no body, the pipeline drains nothing, the file ends up
|
|
19
|
+
* empty.
|
|
20
|
+
*
|
|
21
|
+
* The contract this module enforces:
|
|
22
|
+
*
|
|
23
|
+
* 1. Backend code MUST NOT call `adb` with `["shell", argv1, argv2, ...]`
|
|
24
|
+
* (more than two elements). The only allowed shape is
|
|
25
|
+
* `["shell", "<single command string>"]`. The `assertShellSingleString`
|
|
26
|
+
* helper makes this an executable invariant in tests.
|
|
27
|
+
* 2. The single command string MUST be built with `sh\`...\``
|
|
28
|
+
* (tagged-template) or `shellEscape`. Plain string concatenation of
|
|
29
|
+
* untrusted values into a shell command is forbidden.
|
|
30
|
+
*
|
|
31
|
+
* Both rules are static-friendly: `sh\`...\`` is a tagged template that
|
|
32
|
+
* the type system can spot, and `assertShellSingleString` runs in every
|
|
33
|
+
* unit test through the mock runner. The combination eliminates an entire
|
|
34
|
+
* class of latent injection / silent-corruption bugs (see CHANGELOG 0.2.3).
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* POSIX single-quote escape — the canonical way to embed an arbitrary
|
|
38
|
+
* string into a `/bin/sh` command line.
|
|
39
|
+
*
|
|
40
|
+
* shellEscape("a'b$c") → "'a'\\''b$c'"
|
|
41
|
+
*
|
|
42
|
+
* Single quotes disable every metacharacter inside the quoted span; the
|
|
43
|
+
* only character that needs splicing is `'` itself, replaced by the
|
|
44
|
+
* familiar `'\''` four-byte sequence (close-quote, escaped quote, open).
|
|
45
|
+
*/
|
|
46
|
+
export function shellEscape(s) {
|
|
47
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Tagged template — builds a shell command string with auto-escaped
|
|
51
|
+
* interpolated values. Numbers pass through verbatim (they cannot
|
|
52
|
+
* introduce metacharacters); strings always go through `shellEscape`.
|
|
53
|
+
*
|
|
54
|
+
* sh`am start -n ${component}`
|
|
55
|
+
* // ^ "com.app/.Activity$Sub"
|
|
56
|
+
* // → "am start -n 'com.app/.Activity$Sub'"
|
|
57
|
+
*
|
|
58
|
+
* sh`input swipe ${x1} ${y1} ${x2} ${y2} ${duration}`
|
|
59
|
+
* // → "input swipe 100 200 300 400 250"
|
|
60
|
+
*
|
|
61
|
+
* Use this for every command that has a dynamic piece. Static literal
|
|
62
|
+
* commands (e.g. `wm size`) can be passed as a plain string — but going
|
|
63
|
+
* through `sh\`...\`` is a no-op cost and keeps the call site uniform.
|
|
64
|
+
*/
|
|
65
|
+
export function sh(parts, ...vals) {
|
|
66
|
+
let out = parts[0] ?? "";
|
|
67
|
+
for (let i = 0; i < vals.length; i++) {
|
|
68
|
+
const v = vals[i];
|
|
69
|
+
out +=
|
|
70
|
+
(typeof v === "number" ? String(v) : shellEscape(String(v))) +
|
|
71
|
+
(parts[i + 1] ?? "");
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Test-only invariant — assert that an `adb` argv invocation that targets
|
|
77
|
+
* the `shell` verb uses the single-string pattern (exactly one argument
|
|
78
|
+
* after `"shell"`, optionally preceded by `["-s", serial]`).
|
|
79
|
+
*
|
|
80
|
+
* Throws an `Error` with a descriptive message when the invariant is
|
|
81
|
+
* violated. Production code is expected to satisfy it by construction;
|
|
82
|
+
* the helper exists so tests can wire it through their mock runners and
|
|
83
|
+
* lock the contract from the outside.
|
|
84
|
+
*
|
|
85
|
+
* Examples:
|
|
86
|
+
* assertShellSingleString(["shell", "wm size"]) // ok
|
|
87
|
+
* assertShellSingleString(["-s", "abc", "shell", "wm size"]) // ok
|
|
88
|
+
* assertShellSingleString(["shell", "am", "start"]) // throws
|
|
89
|
+
* assertShellSingleString(["pull", "/x", "/y"]) // ok (not shell)
|
|
90
|
+
*/
|
|
91
|
+
export function assertShellSingleString(args) {
|
|
92
|
+
// Strip optional ["-s", "<serial>"] prefix.
|
|
93
|
+
const rest = args[0] === "-s" ? args.slice(2) : args.slice();
|
|
94
|
+
if (rest.length === 0 || rest[0] !== "shell")
|
|
95
|
+
return;
|
|
96
|
+
if (rest.length !== 2) {
|
|
97
|
+
throw new Error(`adb shell invariant violated: expected ["shell", "<single command>"], ` +
|
|
98
|
+
`got ${JSON.stringify(args)}. Use the sh\`...\` tagged template ` +
|
|
99
|
+
`to build the command instead of passing multiple argv elements.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=adb-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adb-shell.js","sourceRoot":"","sources":["../../src/util/adb-shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,EAAE,CAChB,KAA2B,EAC3B,GAAG,IAAyB;IAE5B,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,GAAG;YACD,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5D,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAuB;IAC7D,4CAA4C;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO;QAAE,OAAO;IACrD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,wEAAwE;YACtE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,sCAAsC;YACjE,iEAAiE,CACpE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android app name → package mapping. Used by `launchApp()` so callers can say
|
|
3
|
+
* "WeChat" instead of "com.tencent.mm". Lifted verbatim from the legacy
|
|
4
|
+
* device-agent's `device/apps.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const APP_PACKAGES: Record<string, string>;
|
|
7
|
+
/** Resolve app name to package, with normalization + fuzzy (substring) fallback. */
|
|
8
|
+
export declare function getAppPackage(appName: string): string | undefined;
|
|
9
|
+
/** Heuristic: looks like an explicit package name (`com.foo.bar`). */
|
|
10
|
+
export declare function looksLikePackage(s: string): boolean;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android app name → package mapping. Used by `launchApp()` so callers can say
|
|
3
|
+
* "WeChat" instead of "com.tencent.mm". Lifted verbatim from the legacy
|
|
4
|
+
* device-agent's `device/apps.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export const APP_PACKAGES = {
|
|
7
|
+
// Social & Messaging
|
|
8
|
+
"微信": "com.tencent.mm",
|
|
9
|
+
WeChat: "com.tencent.mm",
|
|
10
|
+
wechat: "com.tencent.mm",
|
|
11
|
+
QQ: "com.tencent.mobileqq",
|
|
12
|
+
"微博": "com.sina.weibo",
|
|
13
|
+
"钉钉": "com.alibaba.android.rimet",
|
|
14
|
+
DingTalk: "com.alibaba.android.rimet",
|
|
15
|
+
"飞书": "com.ss.android.lark",
|
|
16
|
+
Feishu: "com.ss.android.lark",
|
|
17
|
+
Telegram: "org.telegram.messenger",
|
|
18
|
+
WhatsApp: "com.whatsapp",
|
|
19
|
+
Whatsapp: "com.whatsapp",
|
|
20
|
+
// E-commerce
|
|
21
|
+
"淘宝": "com.taobao.taobao",
|
|
22
|
+
"京东": "com.jingdong.app.mall",
|
|
23
|
+
"拼多多": "com.xunmeng.pinduoduo",
|
|
24
|
+
"小红书": "com.xingin.xhs",
|
|
25
|
+
XiaoHongShu: "com.xingin.xhs",
|
|
26
|
+
xiaohongshu: "com.xingin.xhs",
|
|
27
|
+
RED: "com.xingin.xhs",
|
|
28
|
+
Temu: "com.einnovation.temu",
|
|
29
|
+
temu: "com.einnovation.temu",
|
|
30
|
+
// Food
|
|
31
|
+
"美团": "com.sankuai.meituan",
|
|
32
|
+
"大众点评": "com.dianping.v1",
|
|
33
|
+
"饿了么": "me.ele",
|
|
34
|
+
// Maps
|
|
35
|
+
"高德地图": "com.autonavi.minimap",
|
|
36
|
+
"百度地图": "com.baidu.BaiduMap",
|
|
37
|
+
"滴滴出行": "com.sdu.didi.psnger",
|
|
38
|
+
GoogleMaps: "com.google.android.apps.maps",
|
|
39
|
+
"Google Maps": "com.google.android.apps.maps",
|
|
40
|
+
// Travel
|
|
41
|
+
"携程": "ctrip.android.view",
|
|
42
|
+
"铁路12306": "com.MobileTicket",
|
|
43
|
+
"12306": "com.MobileTicket",
|
|
44
|
+
"去哪儿": "com.Qunar",
|
|
45
|
+
"去哪儿旅行": "com.Qunar",
|
|
46
|
+
// Video & Entertainment
|
|
47
|
+
bilibili: "tv.danmaku.bili",
|
|
48
|
+
"抖音": "com.ss.android.ugc.aweme",
|
|
49
|
+
TikTok: "com.zhiliaoapp.musically",
|
|
50
|
+
"快手": "com.smile.gifmaker",
|
|
51
|
+
"腾讯视频": "com.tencent.qqlive",
|
|
52
|
+
"爱奇艺": "com.qiyi.video",
|
|
53
|
+
"优酷": "com.youku.phone",
|
|
54
|
+
// Music
|
|
55
|
+
"网易云音乐": "com.netease.cloudmusic",
|
|
56
|
+
"QQ音乐": "com.tencent.qqmusic",
|
|
57
|
+
Spotify: "com.spotify.music",
|
|
58
|
+
// Finance
|
|
59
|
+
"支付宝": "com.eg.android.AlipayGphone",
|
|
60
|
+
Alipay: "com.eg.android.AlipayGphone",
|
|
61
|
+
"招商银行": "cmb.pb",
|
|
62
|
+
// Health
|
|
63
|
+
"微信运动": "com.tencent.mm",
|
|
64
|
+
// System
|
|
65
|
+
"设置": "com.android.settings",
|
|
66
|
+
Settings: "com.android.settings",
|
|
67
|
+
"系统设置": "com.android.settings",
|
|
68
|
+
"手机设置": "com.android.settings",
|
|
69
|
+
SystemSettings: "com.android.settings",
|
|
70
|
+
"相机": "com.android.camera2",
|
|
71
|
+
Camera: "com.android.camera2",
|
|
72
|
+
"图库": "com.android.gallery3d",
|
|
73
|
+
Gallery: "com.android.gallery3d",
|
|
74
|
+
// Browsers
|
|
75
|
+
Chrome: "com.android.chrome",
|
|
76
|
+
"夸克浏览器": "com.quark.browser",
|
|
77
|
+
"UC浏览器": "com.UCMobile.intl",
|
|
78
|
+
};
|
|
79
|
+
/** Resolve app name to package, with normalization + fuzzy (substring) fallback. */
|
|
80
|
+
export function getAppPackage(appName) {
|
|
81
|
+
if (!appName)
|
|
82
|
+
return undefined;
|
|
83
|
+
if (APP_PACKAGES[appName])
|
|
84
|
+
return APP_PACKAGES[appName];
|
|
85
|
+
const trimmed = appName.trim();
|
|
86
|
+
if (APP_PACKAGES[trimmed])
|
|
87
|
+
return APP_PACKAGES[trimmed];
|
|
88
|
+
const lower = trimmed.toLowerCase();
|
|
89
|
+
for (const [k, v] of Object.entries(APP_PACKAGES)) {
|
|
90
|
+
if (k.toLowerCase() === lower)
|
|
91
|
+
return v;
|
|
92
|
+
}
|
|
93
|
+
for (const [k, v] of Object.entries(APP_PACKAGES)) {
|
|
94
|
+
if (trimmed.includes(k) || k.includes(trimmed))
|
|
95
|
+
return v;
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
/** Heuristic: looks like an explicit package name (`com.foo.bar`). */
|
|
100
|
+
export function looksLikePackage(s) {
|
|
101
|
+
return /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/i.test(s);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=android-apps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android-apps.js","sourceRoot":"","sources":["../../src/util/android-apps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* image-dim — minimal in-memory image-dimension probes used by the
|
|
3
|
+
* screenshot path.
|
|
4
|
+
*
|
|
5
|
+
* Why hand-roll instead of always going through `sharp`?
|
|
6
|
+
* - The Android / macOS backends already hold raw PNG bytes from
|
|
7
|
+
* `screencap` / `screencapture`. Decoding the entire image just to
|
|
8
|
+
* read 8 bytes off the IHDR chunk is wasted CPU on every frame.
|
|
9
|
+
* - The fast-path also keeps the screenshot pipeline working on tiny
|
|
10
|
+
* fixture buffers in unit tests where calling `sharp` would fail
|
|
11
|
+
* (the test PNGs are not full IDAT-bearing images).
|
|
12
|
+
*
|
|
13
|
+
* Producers that re-encode (resize / convert format) MUST recompute
|
|
14
|
+
* dimensions from `sharp` metadata after the conversion — these helpers
|
|
15
|
+
* are only valid for raw input bytes.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Read width / height from a PNG IHDR chunk.
|
|
19
|
+
*
|
|
20
|
+
* Layout (RFC 2083): 8-byte signature, then a 4-byte length, the
|
|
21
|
+
* "IHDR" chunk type, then the 4-byte width and 4-byte height starting
|
|
22
|
+
* at byte 16. We are deliberately tolerant — anything that does not
|
|
23
|
+
* match the layout falls back to the canonical Android default
|
|
24
|
+
* (`1080×1920`) so the screenshot path continues to function with
|
|
25
|
+
* malformed bytes (the agent layer can still consume the image).
|
|
26
|
+
*/
|
|
27
|
+
export declare function parsePngDimensions(data: Buffer): [number, number];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* image-dim — minimal in-memory image-dimension probes used by the
|
|
3
|
+
* screenshot path.
|
|
4
|
+
*
|
|
5
|
+
* Why hand-roll instead of always going through `sharp`?
|
|
6
|
+
* - The Android / macOS backends already hold raw PNG bytes from
|
|
7
|
+
* `screencap` / `screencapture`. Decoding the entire image just to
|
|
8
|
+
* read 8 bytes off the IHDR chunk is wasted CPU on every frame.
|
|
9
|
+
* - The fast-path also keeps the screenshot pipeline working on tiny
|
|
10
|
+
* fixture buffers in unit tests where calling `sharp` would fail
|
|
11
|
+
* (the test PNGs are not full IDAT-bearing images).
|
|
12
|
+
*
|
|
13
|
+
* Producers that re-encode (resize / convert format) MUST recompute
|
|
14
|
+
* dimensions from `sharp` metadata after the conversion — these helpers
|
|
15
|
+
* are only valid for raw input bytes.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Read width / height from a PNG IHDR chunk.
|
|
19
|
+
*
|
|
20
|
+
* Layout (RFC 2083): 8-byte signature, then a 4-byte length, the
|
|
21
|
+
* "IHDR" chunk type, then the 4-byte width and 4-byte height starting
|
|
22
|
+
* at byte 16. We are deliberately tolerant — anything that does not
|
|
23
|
+
* match the layout falls back to the canonical Android default
|
|
24
|
+
* (`1080×1920`) so the screenshot path continues to function with
|
|
25
|
+
* malformed bytes (the agent layer can still consume the image).
|
|
26
|
+
*/
|
|
27
|
+
export function parsePngDimensions(data) {
|
|
28
|
+
try {
|
|
29
|
+
if (data.length < 24)
|
|
30
|
+
return [1080, 1920];
|
|
31
|
+
return [data.readUInt32BE(16), data.readUInt32BE(20)];
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [1080, 1920];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=image-dim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-dim.js","sourceRoot":"","sources":["../../src/util/image-dim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android uiautomator XML parser + compact LLM-ready formatter.
|
|
3
|
+
*
|
|
4
|
+
* Lifted from the legacy `device-agent/src/device/ui-xml.ts`. Used by the
|
|
5
|
+
* `ui_dump` MCP tool when the caller passes `query` (server-side filtering
|
|
6
|
+
* keeps the wire payload small).
|
|
7
|
+
*
|
|
8
|
+
* Pure data — no IO, no logging, safe to call inside hot paths.
|
|
9
|
+
*/
|
|
10
|
+
import type { UiNode } from "@beeos-ai/device-common";
|
|
11
|
+
/**
|
|
12
|
+
* Format raw uiautomator XML as a compact one-line-per-node summary suitable
|
|
13
|
+
* for VLM prompts.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatUiXmlToolOutput(xml: string, query?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Parse XML and return the structured `UiNode` list (for programmatic use,
|
|
18
|
+
* e.g. ACP `device/ui-tree`).
|
|
19
|
+
*/
|
|
20
|
+
export declare function searchUiXmlNodes(xml: string, query: string): UiNode[];
|