@beeos-ai/device-mcp-server 0.2.3 → 0.4.2
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/dist/backends/android-adb.d.ts +147 -6
- package/dist/backends/android-adb.js +776 -40
- package/dist/backends/android-adb.js.map +1 -1
- package/dist/backends/base.d.ts +243 -7
- package/dist/backends/base.js +81 -2
- package/dist/backends/base.js.map +1 -1
- package/dist/backends/desktop.d.ts +3 -2
- package/dist/backends/desktop.js +9 -3
- package/dist/backends/desktop.js.map +1 -1
- package/dist/backends/linux.js +3 -0
- package/dist/backends/linux.js.map +1 -1
- package/dist/backends/mac.d.ts +11 -2
- package/dist/backends/mac.js +39 -1
- package/dist/backends/mac.js.map +1 -1
- package/dist/backends/stubs/windows.js +3 -0
- package/dist/backends/stubs/windows.js.map +1 -1
- package/dist/cli.d.ts +40 -26
- package/dist/cli.js +118 -84
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- package/dist/server/app.d.ts +60 -17
- package/dist/server/app.js +182 -138
- package/dist/server/app.js.map +1 -1
- package/dist/server/mcp-server.d.ts +25 -0
- package/dist/server/mcp-server.js +33 -0
- package/dist/server/mcp-server.js.map +1 -0
- package/dist/server/registry.d.ts +111 -0
- package/dist/server/registry.js +191 -0
- package/dist/server/registry.js.map +1 -0
- package/dist/server/stdio.d.ts +29 -0
- package/dist/server/stdio.js +35 -0
- package/dist/server/stdio.js.map +1 -0
- package/dist/server/tool-registry.d.ts +60 -35
- package/dist/server/tool-registry.js +911 -434
- package/dist/server/tool-registry.js.map +1 -1
- package/dist/util/adb-files.d.ts +25 -1
- package/dist/util/adb-files.js +95 -0
- package/dist/util/adb-files.js.map +1 -1
- package/dist/util/locale.d.ts +16 -0
- package/dist/util/locale.js +31 -0
- package/dist/util/locale.js.map +1 -0
- package/dist/util/logger.d.ts +27 -0
- package/dist/util/logger.js +27 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/output-path.d.ts +60 -0
- package/dist/util/output-path.js +123 -0
- package/dist/util/output-path.js.map +1 -0
- package/dist/util/package-name.d.ts +26 -0
- package/dist/util/package-name.js +41 -0
- package/dist/util/package-name.js.map +1 -0
- package/package.json +6 -4
- package/dist/backends/stubs/macos.d.ts +0 -13
- package/dist/backends/stubs/macos.js +0 -27
- package/dist/backends/stubs/macos.js.map +0 -1
- package/dist/server/action-mapping.d.ts +0 -21
- package/dist/server/action-mapping.js +0 -153
- package/dist/server/action-mapping.js.map +0 -1
|
@@ -49,10 +49,31 @@
|
|
|
49
49
|
* system). The Android backend no longer compresses by default — the
|
|
50
50
|
* agent layer decides whether to resize.
|
|
51
51
|
*/
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
52
|
+
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
53
|
+
import { DeviceError, type AppInfo, type CrashSummary, type DeviceInfo, type Orientation, type SaveScreenshotResult, type ScreenRecordHandle, type ScreenRecordResult, type UiElement } from "@beeos-ai/device-common";
|
|
54
|
+
import { BaseSandboxBackend, type ExecuteCommandOutput, type LaunchAppOptions, type StartScreenRecordOptions, type ListDirectoryEntry, type ScreenSize, type ScreenshotOptions, type ScreenshotOutput, type UiDumpOutput } from "./base.js";
|
|
54
55
|
import { type AdbRunOptions, type AdbRunResult, type AdbRunner } from "./android-adb-runner.js";
|
|
55
56
|
export type { AdbRunOptions, AdbRunResult, AdbRunner };
|
|
57
|
+
/**
|
|
58
|
+
* Friendly button name → Android `KEYCODE_*`. Names are case-insensitive.
|
|
59
|
+
*
|
|
60
|
+
* The vocabulary mirrors mobile-mcp `mobile_press_button` so prompts that
|
|
61
|
+
* already work against mobile-mcp port over without rewording. The
|
|
62
|
+
* registry exposes `Object.keys(PRESS_BUTTON_KEYMAP)` so the MCP tool's
|
|
63
|
+
* `enum` is auto-derived rather than maintained in two places.
|
|
64
|
+
*/
|
|
65
|
+
export declare const PRESS_BUTTON_KEYMAP: Readonly<Record<string, string>>;
|
|
66
|
+
/**
|
|
67
|
+
* Pluggable spawner used by `screen_record_*` to fork a long-lived
|
|
68
|
+
* `adb shell screenrecord` child. Mirrors `node:child_process.spawn`'s
|
|
69
|
+
* shape — exposed so tests can fake the recording lifecycle without
|
|
70
|
+
* actually invoking adb.
|
|
71
|
+
*
|
|
72
|
+
* Mutable `string[]` (not `readonly`) on purpose — `node:child_process.spawn`
|
|
73
|
+
* itself takes a mutable array, and using `readonly` here would force
|
|
74
|
+
* every implementation through a type assertion.
|
|
75
|
+
*/
|
|
76
|
+
export type AdbProcessSpawner = (file: string, args: string[], opts?: SpawnOptions) => ChildProcess;
|
|
56
77
|
export interface AndroidAdbBackendOptions {
|
|
57
78
|
/** ADB serial, e.g. `emulator-5554`. Optional when a single device is connected. */
|
|
58
79
|
serial?: string;
|
|
@@ -72,6 +93,22 @@ export interface AndroidAdbBackendOptions {
|
|
|
72
93
|
fileTimeoutMs?: number;
|
|
73
94
|
/** Sleep helper, injectable for deterministic tests. */
|
|
74
95
|
sleep?: (ms: number) => Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Long-process spawner for `screen_record_*`. Defaults to
|
|
98
|
+
* `node:child_process.spawn`.
|
|
99
|
+
*/
|
|
100
|
+
processSpawner?: AdbProcessSpawner;
|
|
101
|
+
/**
|
|
102
|
+
* Hard host-side timeout for waiting on a recording to flush after
|
|
103
|
+
* SIGINT before escalating to SIGKILL. Default 5_000 ms.
|
|
104
|
+
*/
|
|
105
|
+
recordKillGraceMs?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Device-side hard cap forwarded as `screenrecord --time-limit`.
|
|
108
|
+
* Default 300 (5 minutes — also adb's own legacy cap so any value
|
|
109
|
+
* above that is silently ignored).
|
|
110
|
+
*/
|
|
111
|
+
recordTimeLimitS?: number;
|
|
75
112
|
}
|
|
76
113
|
export declare class AndroidAdbBackend extends BaseSandboxBackend {
|
|
77
114
|
readonly os: "android";
|
|
@@ -85,11 +122,29 @@ export declare class AndroidAdbBackend extends BaseSandboxBackend {
|
|
|
85
122
|
private readonly screenshotTimeoutMs;
|
|
86
123
|
private readonly installTimeoutMs;
|
|
87
124
|
private readonly fileTimeoutMs;
|
|
125
|
+
private readonly processSpawner;
|
|
126
|
+
private readonly recordKillGraceMs;
|
|
127
|
+
private readonly recordTimeLimitS;
|
|
128
|
+
private readonly recordings;
|
|
129
|
+
/** Cached `android.software.leanback` system feature — drives `info().isTv`. */
|
|
130
|
+
private isLeanback;
|
|
131
|
+
/** True after `probeLeanback()` has resolved (success OR failure). */
|
|
132
|
+
private leanbackProbed;
|
|
88
133
|
constructor(opts?: AndroidAdbBackendOptions);
|
|
89
134
|
connect(): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Detect Android TV / leanback so `info().isTv` can hint at LLM
|
|
137
|
+
* prompts that should default to DPAD navigation. Cached for the
|
|
138
|
+
* lifetime of the backend — running `pm has-feature` once per device
|
|
139
|
+
* is enough; the answer doesn't change at runtime. Lazy on purpose —
|
|
140
|
+
* `connect()` with `skipConnectValidation: true` (the registry path)
|
|
141
|
+
* MUST NOT issue any adb commands, so we defer this probe to the
|
|
142
|
+
* first `info()` call.
|
|
143
|
+
*/
|
|
144
|
+
private ensureLeanbackProbed;
|
|
90
145
|
disconnect(): Promise<void>;
|
|
91
146
|
info(): Promise<DeviceInfo>;
|
|
92
|
-
screenshot(): Promise<ScreenshotOutput>;
|
|
147
|
+
screenshot(opts?: ScreenshotOptions): Promise<ScreenshotOutput>;
|
|
93
148
|
screenSize(): Promise<ScreenSize>;
|
|
94
149
|
uiDump(): Promise<UiDumpOutput>;
|
|
95
150
|
tap(x: number, y: number): Promise<void>;
|
|
@@ -98,13 +153,24 @@ export declare class AndroidAdbBackend extends BaseSandboxBackend {
|
|
|
98
153
|
longPress(x: number, y: number, durationMs?: number): Promise<void>;
|
|
99
154
|
swipe(x1: number, y1: number, x2: number, y2: number, durationMs?: number): Promise<void>;
|
|
100
155
|
drag(x1: number, y1: number, x2: number, y2: number, durationMs?: number): Promise<void>;
|
|
101
|
-
scroll(x: number, y: number, direction: "up" | "down" | "left" | "right",
|
|
156
|
+
scroll(x: number, y: number, direction: "up" | "down" | "left" | "right", amount?: number): Promise<void>;
|
|
102
157
|
typeText(text: string): Promise<void>;
|
|
103
158
|
pressKey(key: string): Promise<void>;
|
|
104
159
|
back(): Promise<void>;
|
|
105
160
|
home(): Promise<void>;
|
|
106
161
|
navigate(direction: "back" | "forward" | "up"): Promise<void>;
|
|
107
|
-
launchApp(pkgOrName: string,
|
|
162
|
+
launchApp(pkgOrName: string, opts?: LaunchAppOptions | string): Promise<void>;
|
|
163
|
+
openUrl(url: string): Promise<void>;
|
|
164
|
+
listApps(opts?: {
|
|
165
|
+
includeSystem?: boolean;
|
|
166
|
+
launchableOnly?: boolean;
|
|
167
|
+
}): Promise<AppInfo[]>;
|
|
168
|
+
terminateApp(pkg: string): Promise<void>;
|
|
169
|
+
listElements(opts?: {
|
|
170
|
+
query?: string;
|
|
171
|
+
}): Promise<UiElement[]>;
|
|
172
|
+
getOrientation(): Promise<Orientation>;
|
|
173
|
+
setOrientation(o: Orientation): Promise<void>;
|
|
108
174
|
executeCommand(body: string, opts?: {
|
|
109
175
|
timeoutS?: number;
|
|
110
176
|
cwd?: string;
|
|
@@ -113,7 +179,55 @@ export declare class AndroidAdbBackend extends BaseSandboxBackend {
|
|
|
113
179
|
fileWrite(path: string, content: Buffer | string): Promise<void>;
|
|
114
180
|
listDirectory(path: string): Promise<ListDirectoryEntry[]>;
|
|
115
181
|
install(path: string): Promise<void>;
|
|
116
|
-
|
|
182
|
+
uninstallApp(pkgOrName: string): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Parse `dumpsys dropbox --print` and return one entry per recorded
|
|
185
|
+
* crash / ANR / WTF. The list is best-effort — the dumpsys output
|
|
186
|
+
* format has shifted slightly across Android releases, so we tolerate
|
|
187
|
+
* unknown lines instead of rejecting them.
|
|
188
|
+
*/
|
|
189
|
+
listCrashes(): Promise<CrashSummary[]>;
|
|
190
|
+
/**
|
|
191
|
+
* Re-run `dumpsys dropbox --print` and return the body of the entry
|
|
192
|
+
* whose `id` matches. Stateless so the result stays correct even if
|
|
193
|
+
* `listCrashes` was called from a different MCP session.
|
|
194
|
+
*/
|
|
195
|
+
getCrash(id: string): Promise<string>;
|
|
196
|
+
/**
|
|
197
|
+
* Spawn `adb shell screenrecord` for a single recording. The device
|
|
198
|
+
* enforces a hard `--time-limit` (default 5 min, capped at 300s by
|
|
199
|
+
* Android itself); the host sends SIGINT on `stopScreenRecord` and
|
|
200
|
+
* falls back to SIGKILL after `recordKillGraceMs` if the child
|
|
201
|
+
* hasn't exited.
|
|
202
|
+
*
|
|
203
|
+
* The same-pid `screenrecord` invocation also lets us pipe the output
|
|
204
|
+
* to a `/sdcard/<id>.mp4` so `adb pull` after stop can fetch the
|
|
205
|
+
* exact file without guessing names.
|
|
206
|
+
*
|
|
207
|
+
* `opts.timeLimitS` (mobile-mcp parity, r3): caller-supplied cap,
|
|
208
|
+
* clamped into `[1, 300]` and passed to `screenrecord --time-limit`.
|
|
209
|
+
* `opts.localPath`: caller-supplied host-side destination registered
|
|
210
|
+
* at start time. Already passed through `assertSafeOutputPathWithExt`
|
|
211
|
+
* — backend treats as trusted.
|
|
212
|
+
*/
|
|
213
|
+
startScreenRecord(opts?: StartScreenRecordOptions): Promise<ScreenRecordHandle>;
|
|
214
|
+
stopScreenRecord(id: string, opts?: {
|
|
215
|
+
path?: string;
|
|
216
|
+
}): Promise<ScreenRecordResult>;
|
|
217
|
+
/**
|
|
218
|
+
* Capture a screenshot via `adb pull` of `/sdcard/screenshot.png`.
|
|
219
|
+
* Bypasses the JSON-RPC body cap by writing the bytes straight to a
|
|
220
|
+
* host filesystem path the caller chose. Useful for 4K Android
|
|
221
|
+
* captures where base64-over-HTTP would otherwise blow past 16 MB.
|
|
222
|
+
*/
|
|
223
|
+
saveScreenshot(localPath: string, opts?: ScreenshotOptions): Promise<SaveScreenshotResult>;
|
|
224
|
+
/**
|
|
225
|
+
* Count the number of displays the device exposes. Reads
|
|
226
|
+
* `dumpsys SurfaceFlinger --display-id` (Android 10+) and falls back
|
|
227
|
+
* to "1" on older builds where the flag isn't recognised.
|
|
228
|
+
*/
|
|
229
|
+
getDisplayCount(): Promise<number>;
|
|
230
|
+
getCurrentApp(): Promise<string>;
|
|
117
231
|
private getCurrentIme;
|
|
118
232
|
private setIme;
|
|
119
233
|
private clearText;
|
|
@@ -151,3 +265,30 @@ export declare function listAdbDevices(runner?: AdbRunner, timeoutMs?: number):
|
|
|
151
265
|
* a `DeviceError` in another `DeviceError`.
|
|
152
266
|
*/
|
|
153
267
|
export declare function wrapAdbFailure(verb: string, args: string[], cause: unknown, timeoutMs?: number): DeviceError;
|
|
268
|
+
export declare function assertSafeUrl(url: string): void;
|
|
269
|
+
/**
|
|
270
|
+
* Parse the textual output of `dumpsys dropbox --print` into one
|
|
271
|
+
* `CrashSummary` per crash / ANR / WTF entry.
|
|
272
|
+
*
|
|
273
|
+
* Output shape (Android 7+):
|
|
274
|
+
*
|
|
275
|
+
* 2024-08-15 12:34:56 system_app_crash (text, 1234 bytes):
|
|
276
|
+
* Process: com.foo.bar
|
|
277
|
+
* ...stack trace lines...
|
|
278
|
+
* 2024-08-15 12:35:00 data_app_anr (text, 5678 bytes):
|
|
279
|
+
* ...
|
|
280
|
+
*
|
|
281
|
+
* We extract the leading `<timestamp> <tag> (text, N bytes):` row and
|
|
282
|
+
* include the first non-blank body line as a `headline` for at-a-glance
|
|
283
|
+
* triage. The id encodes both the tag and the timestamp so `getCrash`
|
|
284
|
+
* can re-locate the same row on the next `dumpsys` invocation without
|
|
285
|
+
* holding state.
|
|
286
|
+
*/
|
|
287
|
+
export declare function parseDropboxEntries(out: string): CrashSummary[];
|
|
288
|
+
/**
|
|
289
|
+
* Pull the body of a single dumpsys-dropbox entry out of the `--print`
|
|
290
|
+
* blob, indexed by `<tag>:<timestamp>` (the same id `parseDropboxEntries`
|
|
291
|
+
* returns). The body is everything between the matching header and the
|
|
292
|
+
* next entry header.
|
|
293
|
+
*/
|
|
294
|
+
export declare function extractDropboxBody(out: string, id: string): string | undefined;
|