@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.
Files changed (62) hide show
  1. package/LICENSE +201 -0
  2. package/dist/backends/android-adb-runner.d.ts +32 -0
  3. package/dist/backends/android-adb-runner.js +15 -0
  4. package/dist/backends/android-adb-runner.js.map +1 -0
  5. package/dist/backends/android-adb.d.ts +153 -0
  6. package/dist/backends/android-adb.js +723 -0
  7. package/dist/backends/android-adb.js.map +1 -0
  8. package/dist/backends/base.d.ts +150 -0
  9. package/dist/backends/base.js +116 -0
  10. package/dist/backends/base.js.map +1 -0
  11. package/dist/backends/desktop.d.ts +62 -0
  12. package/dist/backends/desktop.js +176 -0
  13. package/dist/backends/desktop.js.map +1 -0
  14. package/dist/backends/index.d.ts +63 -0
  15. package/dist/backends/index.js +105 -0
  16. package/dist/backends/index.js.map +1 -0
  17. package/dist/backends/linux.d.ts +69 -0
  18. package/dist/backends/linux.js +230 -0
  19. package/dist/backends/linux.js.map +1 -0
  20. package/dist/backends/mac.d.ts +154 -0
  21. package/dist/backends/mac.js +881 -0
  22. package/dist/backends/mac.js.map +1 -0
  23. package/dist/backends/stubs/ios.d.ts +17 -0
  24. package/dist/backends/stubs/ios.js +32 -0
  25. package/dist/backends/stubs/ios.js.map +1 -0
  26. package/dist/backends/stubs/macos.d.ts +13 -0
  27. package/dist/backends/stubs/macos.js +27 -0
  28. package/dist/backends/stubs/macos.js.map +1 -0
  29. package/dist/backends/stubs/windows.d.ts +69 -0
  30. package/dist/backends/stubs/windows.js +191 -0
  31. package/dist/backends/stubs/windows.js.map +1 -0
  32. package/dist/cli.d.ts +37 -0
  33. package/dist/cli.js +177 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.js +14 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/server/action-mapping.d.ts +21 -0
  39. package/dist/server/action-mapping.js +153 -0
  40. package/dist/server/action-mapping.js.map +1 -0
  41. package/dist/server/app.d.ts +23 -0
  42. package/dist/server/app.js +157 -0
  43. package/dist/server/app.js.map +1 -0
  44. package/dist/server/tool-registry.d.ts +50 -0
  45. package/dist/server/tool-registry.js +504 -0
  46. package/dist/server/tool-registry.js.map +1 -0
  47. package/dist/util/adb-files.d.ts +92 -0
  48. package/dist/util/adb-files.js +221 -0
  49. package/dist/util/adb-files.js.map +1 -0
  50. package/dist/util/adb-shell.d.ts +80 -0
  51. package/dist/util/adb-shell.js +102 -0
  52. package/dist/util/adb-shell.js.map +1 -0
  53. package/dist/util/android-apps.d.ts +10 -0
  54. package/dist/util/android-apps.js +103 -0
  55. package/dist/util/android-apps.js.map +1 -0
  56. package/dist/util/image-dim.d.ts +27 -0
  57. package/dist/util/image-dim.js +37 -0
  58. package/dist/util/image-dim.js.map +1 -0
  59. package/dist/util/ui-xml.d.ts +20 -0
  60. package/dist/util/ui-xml.js +184 -0
  61. package/dist/util/ui-xml.js.map +1 -0
  62. 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[];