@0xhayd3n/fling 0.6.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/dist/adb.d.ts +18 -0
- package/dist/adb.js +109 -0
- package/dist/adb.js.map +1 -0
- package/dist/apkFinder.d.ts +19 -0
- package/dist/apkFinder.js +113 -0
- package/dist/apkFinder.js.map +1 -0
- package/dist/apkResolver.d.ts +14 -0
- package/dist/apkResolver.js +55 -0
- package/dist/apkResolver.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.js +113 -0
- package/dist/config.js.map +1 -0
- package/dist/devices.d.ts +24 -0
- package/dist/devices.js +143 -0
- package/dist/devices.js.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/featureFlags.d.ts +6 -0
- package/dist/featureFlags.js +11 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/gradle.d.ts +39 -0
- package/dist/gradle.js +129 -0
- package/dist/gradle.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/mdns.d.ts +19 -0
- package/dist/mdns.js +68 -0
- package/dist/mdns.js.map +1 -0
- package/dist/pairing.d.ts +52 -0
- package/dist/pairing.js +212 -0
- package/dist/pairing.js.map +1 -0
- package/dist/qrText.d.ts +5 -0
- package/dist/qrText.js +8 -0
- package/dist/qrText.js.map +1 -0
- package/dist/schemas.d.ts +31 -0
- package/dist/schemas.js +26 -0
- package/dist/schemas.js.map +1 -0
- package/dist/shellFraming.d.ts +28 -0
- package/dist/shellFraming.js +43 -0
- package/dist/shellFraming.js.map +1 -0
- package/dist/shellPool.d.ts +54 -0
- package/dist/shellPool.js +181 -0
- package/dist/shellPool.js.map +1 -0
- package/dist/toolResult.d.ts +10 -0
- package/dist/toolResult.js +11 -0
- package/dist/toolResult.js.map +1 -0
- package/dist/tools/build-app.d.ts +2 -0
- package/dist/tools/build-app.js +74 -0
- package/dist/tools/build-app.js.map +1 -0
- package/dist/tools/deploy-and-run.d.ts +2 -0
- package/dist/tools/deploy-and-run.js +195 -0
- package/dist/tools/deploy-and-run.js.map +1 -0
- package/dist/tools/device-state.d.ts +11 -0
- package/dist/tools/device-state.js +140 -0
- package/dist/tools/device-state.js.map +1 -0
- package/dist/tools/dismiss-dialog.d.ts +15 -0
- package/dist/tools/dismiss-dialog.js +89 -0
- package/dist/tools/dismiss-dialog.js.map +1 -0
- package/dist/tools/dump-ui.d.ts +4 -0
- package/dist/tools/dump-ui.js +93 -0
- package/dist/tools/dump-ui.js.map +1 -0
- package/dist/tools/find-on-screen.d.ts +27 -0
- package/dist/tools/find-on-screen.js +92 -0
- package/dist/tools/find-on-screen.js.map +1 -0
- package/dist/tools/install-app.d.ts +23 -0
- package/dist/tools/install-app.js +127 -0
- package/dist/tools/install-app.js.map +1 -0
- package/dist/tools/launch-and-wait.d.ts +27 -0
- package/dist/tools/launch-and-wait.js +103 -0
- package/dist/tools/launch-and-wait.js.map +1 -0
- package/dist/tools/launch-app.d.ts +20 -0
- package/dist/tools/launch-app.js +131 -0
- package/dist/tools/launch-app.js.map +1 -0
- package/dist/tools/launch-settings.d.ts +42 -0
- package/dist/tools/launch-settings.js +211 -0
- package/dist/tools/launch-settings.js.map +1 -0
- package/dist/tools/list-devices.d.ts +2 -0
- package/dist/tools/list-devices.js +35 -0
- package/dist/tools/list-devices.js.map +1 -0
- package/dist/tools/long-press-by-text.d.ts +3 -0
- package/dist/tools/long-press-by-text.js +99 -0
- package/dist/tools/long-press-by-text.js.map +1 -0
- package/dist/tools/open-setting.d.ts +55 -0
- package/dist/tools/open-setting.js +257 -0
- package/dist/tools/open-setting.js.map +1 -0
- package/dist/tools/read-logs.d.ts +2 -0
- package/dist/tools/read-logs.js +147 -0
- package/dist/tools/read-logs.js.map +1 -0
- package/dist/tools/screenshot-with-ui.d.ts +21 -0
- package/dist/tools/screenshot-with-ui.js +74 -0
- package/dist/tools/screenshot-with-ui.js.map +1 -0
- package/dist/tools/screenshot.d.ts +8 -0
- package/dist/tools/screenshot.js +97 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/scroll-until-visible.d.ts +23 -0
- package/dist/tools/scroll-until-visible.js +138 -0
- package/dist/tools/scroll-until-visible.js.map +1 -0
- package/dist/tools/start-pair-qr.d.ts +11 -0
- package/dist/tools/start-pair-qr.js +62 -0
- package/dist/tools/start-pair-qr.js.map +1 -0
- package/dist/tools/stop-app.d.ts +2 -0
- package/dist/tools/stop-app.js +63 -0
- package/dist/tools/stop-app.js.map +1 -0
- package/dist/tools/tap-by-content-desc.d.ts +17 -0
- package/dist/tools/tap-by-content-desc.js +97 -0
- package/dist/tools/tap-by-content-desc.js.map +1 -0
- package/dist/tools/tap-by-resource-id.d.ts +17 -0
- package/dist/tools/tap-by-resource-id.js +89 -0
- package/dist/tools/tap-by-resource-id.js.map +1 -0
- package/dist/tools/tap-by-text.d.ts +40 -0
- package/dist/tools/tap-by-text.js +180 -0
- package/dist/tools/tap-by-text.js.map +1 -0
- package/dist/tools/tap-text-verified.d.ts +37 -0
- package/dist/tools/tap-text-verified.js +111 -0
- package/dist/tools/tap-text-verified.js.map +1 -0
- package/dist/tools/uninstall-app.d.ts +2 -0
- package/dist/tools/uninstall-app.js +96 -0
- package/dist/tools/uninstall-app.js.map +1 -0
- package/dist/tools/wait-for-pair.d.ts +29 -0
- package/dist/tools/wait-for-pair.js +132 -0
- package/dist/tools/wait-for-pair.js.map +1 -0
- package/dist/tools/wait-for.d.ts +26 -0
- package/dist/tools/wait-for.js +109 -0
- package/dist/tools/wait-for.js.map +1 -0
- package/dist/uiDump.d.ts +34 -0
- package/dist/uiDump.js +108 -0
- package/dist/uiDump.js.map +1 -0
- package/dist/uiSelector.d.ts +21 -0
- package/dist/uiSelector.js +62 -0
- package/dist/uiSelector.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runAdb } from "../adb.js";
|
|
3
|
+
import { resolveDeviceArgs } from "../devices.js";
|
|
4
|
+
import { FlingError } from "../errors.js";
|
|
5
|
+
import { deviceIdInput } from "../schemas.js";
|
|
6
|
+
import { toolErrorFrom } from "../toolResult.js";
|
|
7
|
+
const PANEL_TO_SUFFIX = {
|
|
8
|
+
wifi: "WIFI_SETTINGS",
|
|
9
|
+
bluetooth: "BLUETOOTH_SETTINGS",
|
|
10
|
+
apps: "APPLICATION_SETTINGS",
|
|
11
|
+
display: "DISPLAY_SETTINGS",
|
|
12
|
+
sound: "SOUND_SETTINGS",
|
|
13
|
+
battery: "BATTERY_SAVER_SETTINGS",
|
|
14
|
+
storage: "INTERNAL_STORAGE_SETTINGS",
|
|
15
|
+
location: "LOCATION_SOURCE_SETTINGS",
|
|
16
|
+
security: "SECURITY_SETTINGS",
|
|
17
|
+
developer: "APPLICATION_DEVELOPMENT_SETTINGS",
|
|
18
|
+
about: "DEVICE_INFO_SETTINGS",
|
|
19
|
+
date: "DATE_SETTINGS",
|
|
20
|
+
language: "LOCALE_SETTINGS",
|
|
21
|
+
accessibility: "ACCESSIBILITY_SETTINGS",
|
|
22
|
+
notifications: "NOTIFICATION_SETTINGS",
|
|
23
|
+
};
|
|
24
|
+
const PANEL_NAMES = Object.keys(PANEL_TO_SUFFIX);
|
|
25
|
+
const ACTION_PREFIX = "android.settings.";
|
|
26
|
+
const SUFFIX_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
27
|
+
const DATA_URI_RE = /^package:[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
|
|
28
|
+
// Standard android.provider.Settings.ACTION_* values that point at a built-in
|
|
29
|
+
// Settings screen reachable via `am start -a android.settings.<X>`. Restricted
|
|
30
|
+
// to a known set to avoid arbitrary intent dispatch through this tool.
|
|
31
|
+
export const SETTINGS_ACTION_ALLOWLIST = new Set([
|
|
32
|
+
"ACCESSIBILITY_SETTINGS",
|
|
33
|
+
"AIRPLANE_MODE_SETTINGS",
|
|
34
|
+
"APN_SETTINGS",
|
|
35
|
+
"APPLICATION_DETAILS_SETTINGS",
|
|
36
|
+
"APPLICATION_DEVELOPMENT_SETTINGS",
|
|
37
|
+
"APPLICATION_SETTINGS",
|
|
38
|
+
"APP_NOTIFICATION_SETTINGS",
|
|
39
|
+
"BATTERY_SAVER_SETTINGS",
|
|
40
|
+
"BLUETOOTH_SETTINGS",
|
|
41
|
+
"CAPTIONING_SETTINGS",
|
|
42
|
+
"CAST_SETTINGS",
|
|
43
|
+
"DATA_ROAMING_SETTINGS",
|
|
44
|
+
"DATE_SETTINGS",
|
|
45
|
+
"DEVICE_INFO_SETTINGS",
|
|
46
|
+
"DISPLAY_SETTINGS",
|
|
47
|
+
"DREAM_SETTINGS",
|
|
48
|
+
"HARD_KEYBOARD_SETTINGS",
|
|
49
|
+
"HOME_SETTINGS",
|
|
50
|
+
"IGNORE_BATTERY_OPTIMIZATION_SETTINGS",
|
|
51
|
+
"INPUT_METHOD_SETTINGS",
|
|
52
|
+
"INPUT_METHOD_SUBTYPE_SETTINGS",
|
|
53
|
+
"INTERNAL_STORAGE_SETTINGS",
|
|
54
|
+
"LOCALE_SETTINGS",
|
|
55
|
+
"LOCATION_SOURCE_SETTINGS",
|
|
56
|
+
"MANAGE_ALL_APPLICATIONS_SETTINGS",
|
|
57
|
+
"MANAGE_APPLICATIONS_SETTINGS",
|
|
58
|
+
"MEMORY_CARD_SETTINGS",
|
|
59
|
+
"NETWORK_OPERATOR_SETTINGS",
|
|
60
|
+
"NFC_PAYMENT_SETTINGS",
|
|
61
|
+
"NFC_SETTINGS",
|
|
62
|
+
"NFCSHARING_SETTINGS",
|
|
63
|
+
"NOTIFICATION_SETTINGS",
|
|
64
|
+
"PRIVACY_SETTINGS",
|
|
65
|
+
"QUICK_LAUNCH_SETTINGS",
|
|
66
|
+
"SEARCH_SETTINGS",
|
|
67
|
+
"SECURITY_SETTINGS",
|
|
68
|
+
"SETTINGS",
|
|
69
|
+
"SOUND_SETTINGS",
|
|
70
|
+
"SYNC_SETTINGS",
|
|
71
|
+
"TETHER_SETTINGS",
|
|
72
|
+
"USAGE_ACCESS_SETTINGS",
|
|
73
|
+
"USER_DICTIONARY_SETTINGS",
|
|
74
|
+
"VOICE_INPUT_SETTINGS",
|
|
75
|
+
"VPN_SETTINGS",
|
|
76
|
+
"WIFI_IP_SETTINGS",
|
|
77
|
+
"WIFI_SETTINGS",
|
|
78
|
+
"WIRELESS_SETTINGS",
|
|
79
|
+
]);
|
|
80
|
+
export function panelToAction(panel) {
|
|
81
|
+
const suffix = PANEL_TO_SUFFIX[panel];
|
|
82
|
+
if (!suffix) {
|
|
83
|
+
throw new FlingError("INVALID_INPUT", `Unknown settings panel: ${panel}`);
|
|
84
|
+
}
|
|
85
|
+
return `android.settings.${suffix}`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Accepts either the bare suffix (e.g. "WIFI_SETTINGS") or the fully qualified
|
|
89
|
+
* action (e.g. "android.settings.WIFI_SETTINGS") and returns the fully
|
|
90
|
+
* qualified form. Throws FlingError("INVALID_INPUT") on anything else — most
|
|
91
|
+
* importantly, on actions outside the allowlist, which is the guard against
|
|
92
|
+
* using this tool to fire arbitrary intents.
|
|
93
|
+
*/
|
|
94
|
+
export function normalizeSettingsAction(input) {
|
|
95
|
+
const trimmed = input.trim();
|
|
96
|
+
if (!trimmed) {
|
|
97
|
+
throw new FlingError("INVALID_INPUT", "Settings action is required.");
|
|
98
|
+
}
|
|
99
|
+
const suffix = trimmed.startsWith(ACTION_PREFIX)
|
|
100
|
+
? trimmed.slice(ACTION_PREFIX.length)
|
|
101
|
+
: trimmed;
|
|
102
|
+
if (!SUFFIX_RE.test(suffix)) {
|
|
103
|
+
throw new FlingError("INVALID_INPUT", `Invalid settings action: "${input}". Expected an uppercase suffix like WIFI_SETTINGS, optionally prefixed with android.settings..`);
|
|
104
|
+
}
|
|
105
|
+
if (!SETTINGS_ACTION_ALLOWLIST.has(suffix)) {
|
|
106
|
+
throw new FlingError("INVALID_INPUT", `Settings action "${suffix}" is not in the allowlist. Known actions: ${[...SETTINGS_ACTION_ALLOWLIST].sort().join(", ")}.`);
|
|
107
|
+
}
|
|
108
|
+
return ACTION_PREFIX + suffix;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Strictly validates the `-d` payload. The only documented use for a data URI
|
|
112
|
+
* on a built-in settings intent is APPLICATION_DETAILS_SETTINGS, which expects
|
|
113
|
+
* `package:<dotted-android-id>`. Anything else is rejected to prevent arbitrary
|
|
114
|
+
* URI dispatch via this tool.
|
|
115
|
+
*/
|
|
116
|
+
export function validateSettingsDataUri(uri) {
|
|
117
|
+
if (!DATA_URI_RE.test(uri)) {
|
|
118
|
+
throw new FlingError("INVALID_INPUT", `Invalid data_uri: "${uri}". Only package:<dotted-android-id> is accepted (e.g. package:com.example.app).`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export function buildSettingsAmArgs(params) {
|
|
122
|
+
// -W makes `am start` wait for the activity to come up and print Status: ok;
|
|
123
|
+
// without it the success line is suppressed and we can't tell whether the
|
|
124
|
+
// dispatch actually resolved an activity.
|
|
125
|
+
const argv = [
|
|
126
|
+
...params.deviceArgs,
|
|
127
|
+
"shell",
|
|
128
|
+
"am",
|
|
129
|
+
"start",
|
|
130
|
+
"-W",
|
|
131
|
+
"-a",
|
|
132
|
+
params.action,
|
|
133
|
+
];
|
|
134
|
+
if (params.dataUri) {
|
|
135
|
+
argv.push("-d", params.dataUri);
|
|
136
|
+
}
|
|
137
|
+
return argv;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Interpret `am start` output. The activity manager merges device-side stderr
|
|
141
|
+
* into adb stdout in some Android builds, so we concatenate both and look for
|
|
142
|
+
* known failure markers OR the absence of `Status: ok` (which `am start -W`
|
|
143
|
+
* always emits on success).
|
|
144
|
+
*/
|
|
145
|
+
export function interpretSettingsResult(stdout, stderr) {
|
|
146
|
+
const combined = `${stdout}\n${stderr}`;
|
|
147
|
+
const failureRe = /Error: (?:Activity (?:class|not started)|Intent does not match|.+ not found)|java\.lang\.SecurityException|No Activity found to handle/;
|
|
148
|
+
const successRe = /Status:\s*ok/i;
|
|
149
|
+
if (failureRe.test(combined) || !successRe.test(combined)) {
|
|
150
|
+
const reason = combined.trim().split(/\r?\n/).slice(0, 8).join("\n");
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
message: "Settings launch dispatch failed.",
|
|
154
|
+
raw: reason,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
message: "Settings launch dispatched.",
|
|
160
|
+
raw: combined.trim(),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export async function performOpenSetting(params) {
|
|
164
|
+
const { stdout, stderr } = await runAdb(buildSettingsAmArgs(params));
|
|
165
|
+
return interpretSettingsResult(stdout, stderr);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Resolve the user's panel-or-action input into the fully qualified
|
|
169
|
+
* android.settings.* action string. Enforces XOR — exactly one of
|
|
170
|
+
* {panel, action} must be set. Delegates value validation to panelToAction
|
|
171
|
+
* (unknown panel) and normalizeSettingsAction (unknown / malformed /
|
|
172
|
+
* non-allowlisted action), each of which throws INVALID_INPUT on rejection.
|
|
173
|
+
*/
|
|
174
|
+
export function resolveOpenSettingAction(input) {
|
|
175
|
+
if (!input.panel && !input.action) {
|
|
176
|
+
throw new FlingError("INVALID_INPUT", "Provide either `panel` or `action`.");
|
|
177
|
+
}
|
|
178
|
+
if (input.panel && input.action) {
|
|
179
|
+
throw new FlingError("INVALID_INPUT", "Provide either `panel` or `action`, not both.");
|
|
180
|
+
}
|
|
181
|
+
return input.panel
|
|
182
|
+
? panelToAction(input.panel)
|
|
183
|
+
: normalizeSettingsAction(input.action);
|
|
184
|
+
}
|
|
185
|
+
export function registerOpenSetting(server) {
|
|
186
|
+
server.registerTool("open_setting", {
|
|
187
|
+
title: "Open an Android Settings screen by intent",
|
|
188
|
+
description: "Open a built-in Android Settings screen via `am start -a android.settings.<ACTION>`. " +
|
|
189
|
+
"Pass either `panel` (friendly name: wifi, bluetooth, apps, display, sound, " +
|
|
190
|
+
"battery, storage, location, security, developer, about, date, language, " +
|
|
191
|
+
"accessibility, notifications) OR `action` (an allowlisted Settings action like " +
|
|
192
|
+
"WIFI_SETTINGS or the fully qualified android.settings.WIFI_SETTINGS). " +
|
|
193
|
+
"`data_uri` is for actions that need a target, currently restricted to " +
|
|
194
|
+
"package:<dotted-android-id> (the canonical form for APPLICATION_DETAILS_SETTINGS). " +
|
|
195
|
+
"Much faster than tap-walking the Settings UI — one adb call vs ~10 round-trips.",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
device_id: deviceIdInput,
|
|
198
|
+
panel: z
|
|
199
|
+
.enum(PANEL_NAMES)
|
|
200
|
+
.optional()
|
|
201
|
+
.describe("Friendly panel name. Mutually exclusive with `action`."),
|
|
202
|
+
action: z
|
|
203
|
+
.string()
|
|
204
|
+
.min(1)
|
|
205
|
+
.optional()
|
|
206
|
+
.describe("Allowlisted Settings action. Bare suffix (WIFI_SETTINGS) or fully qualified (android.settings.WIFI_SETTINGS). Mutually exclusive with `panel`."),
|
|
207
|
+
data_uri: z
|
|
208
|
+
.string()
|
|
209
|
+
.min(1)
|
|
210
|
+
.optional()
|
|
211
|
+
.describe("Optional `-d` payload. Must be of the form package:<dotted-id>, e.g. package:com.example.app for APPLICATION_DETAILS_SETTINGS."),
|
|
212
|
+
},
|
|
213
|
+
outputSchema: {
|
|
214
|
+
device_id: z.string(),
|
|
215
|
+
action: z.string(),
|
|
216
|
+
data_uri: z.string().optional(),
|
|
217
|
+
success: z.boolean(),
|
|
218
|
+
message: z.string(),
|
|
219
|
+
},
|
|
220
|
+
annotations: {
|
|
221
|
+
readOnlyHint: false,
|
|
222
|
+
destructiveHint: false,
|
|
223
|
+
idempotentHint: true,
|
|
224
|
+
openWorldHint: false,
|
|
225
|
+
},
|
|
226
|
+
}, async ({ device_id, panel, action, data_uri }) => {
|
|
227
|
+
try {
|
|
228
|
+
const fullAction = resolveOpenSettingAction({ panel, action });
|
|
229
|
+
if (data_uri !== undefined)
|
|
230
|
+
validateSettingsDataUri(data_uri);
|
|
231
|
+
const { args: deviceArgs, serial } = await resolveDeviceArgs(device_id);
|
|
232
|
+
const result = await performOpenSetting({
|
|
233
|
+
action: fullAction,
|
|
234
|
+
dataUri: data_uri,
|
|
235
|
+
deviceArgs,
|
|
236
|
+
});
|
|
237
|
+
const text = result.success
|
|
238
|
+
? `${result.message} (${fullAction}) on ${serial}.`
|
|
239
|
+
: `${result.message} (${fullAction} on ${serial})\n\n${result.raw}`;
|
|
240
|
+
return {
|
|
241
|
+
content: [{ type: "text", text }],
|
|
242
|
+
structuredContent: {
|
|
243
|
+
device_id: serial,
|
|
244
|
+
action: fullAction,
|
|
245
|
+
data_uri,
|
|
246
|
+
success: result.success,
|
|
247
|
+
message: text,
|
|
248
|
+
},
|
|
249
|
+
isError: !result.success,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
return toolErrorFrom(err);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=open-setting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-setting.js","sourceRoot":"","sources":["../../src/tools/open-setting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runAdb } from "../adb.js";
|
|
3
|
+
import { resolveDeviceArgs } from "../devices.js";
|
|
4
|
+
import { FlingError } from "../errors.js";
|
|
5
|
+
import { deviceIdInput } from "../schemas.js";
|
|
6
|
+
import { toolErrorFrom } from "../toolResult.js";
|
|
7
|
+
const LOGCAT_TIMEOUT_MS = 30_000;
|
|
8
|
+
const LOGCAT_BUFFER_BYTES = 20 * 1024 * 1024;
|
|
9
|
+
const PACKAGE_RE = /^[A-Za-z][\w]*(\.[A-Za-z][\w]*)+$/;
|
|
10
|
+
const TAG_RE = /^[A-Za-z0-9_.+\-]+$/;
|
|
11
|
+
function validatePackage(name) {
|
|
12
|
+
if (!PACKAGE_RE.test(name)) {
|
|
13
|
+
throw new FlingError("INVALID_INPUT", `Invalid Android package name: "${name}".`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateTag(tag) {
|
|
17
|
+
if (!TAG_RE.test(tag)) {
|
|
18
|
+
throw new FlingError("INVALID_INPUT", `Invalid logcat tag: "${tag}". Allowed: letters, digits, underscore, dot, plus, hyphen.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve PIDs for a package via the device shell.
|
|
23
|
+
*
|
|
24
|
+
* Uses `pidof` (toybox, Android 6+/API 23+). On older devices pidof is
|
|
25
|
+
* absent and this returns []; callers will treat that as "app not running"
|
|
26
|
+
* which can be a false negative on pre-API-23 hardware.
|
|
27
|
+
*/
|
|
28
|
+
async function resolvePids(deviceArgs, packageName) {
|
|
29
|
+
const { stdout } = await runAdb([...deviceArgs, "shell", `pidof ${packageName} 2>/dev/null || true`], { timeoutMs: 10_000 });
|
|
30
|
+
return stdout.trim().split(/\s+/).filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
export function registerReadLogs(server) {
|
|
33
|
+
server.registerTool("read_logs", {
|
|
34
|
+
title: "Read recent device logs",
|
|
35
|
+
description: "Dump the last N lines of `adb logcat`, optionally filtered by app package (via PID), " +
|
|
36
|
+
"by logcat tag, or by minimum priority. Returns a single snapshot — does not stream.",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
package_name: z
|
|
39
|
+
.string()
|
|
40
|
+
.min(1)
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Filter to logs from this app's process(es). Resolved via `pidof` on the device. " +
|
|
43
|
+
"Returns an empty result with success=false if the app isn't currently running."),
|
|
44
|
+
tag: z
|
|
45
|
+
.string()
|
|
46
|
+
.min(1)
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Restrict to entries with this logcat tag."),
|
|
49
|
+
priority: z
|
|
50
|
+
.enum(["V", "D", "I", "W", "E", "F"])
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Minimum log priority (V=verbose, D=debug, I=info, W=warn, E=error, F=fatal). Default V."),
|
|
53
|
+
lines: z
|
|
54
|
+
.number()
|
|
55
|
+
.int()
|
|
56
|
+
.min(1)
|
|
57
|
+
.max(5000)
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Number of trailing lines to return. Default 200, max 5000."),
|
|
60
|
+
device_id: deviceIdInput,
|
|
61
|
+
},
|
|
62
|
+
outputSchema: {
|
|
63
|
+
device_id: z.string(),
|
|
64
|
+
package_name: z.string().optional(),
|
|
65
|
+
pids: z.array(z.string()).optional(),
|
|
66
|
+
lines_returned: z.number().int().nonnegative(),
|
|
67
|
+
logs: z.string(),
|
|
68
|
+
},
|
|
69
|
+
annotations: {
|
|
70
|
+
readOnlyHint: true,
|
|
71
|
+
openWorldHint: false,
|
|
72
|
+
},
|
|
73
|
+
}, async ({ package_name, tag, priority, lines, device_id }) => {
|
|
74
|
+
try {
|
|
75
|
+
if (package_name)
|
|
76
|
+
validatePackage(package_name);
|
|
77
|
+
if (tag)
|
|
78
|
+
validateTag(tag);
|
|
79
|
+
const { args: deviceArgs, serial } = await resolveDeviceArgs(device_id);
|
|
80
|
+
const lineCount = lines ?? 200;
|
|
81
|
+
let pids;
|
|
82
|
+
if (package_name) {
|
|
83
|
+
pids = await resolvePids(deviceArgs, package_name);
|
|
84
|
+
if (pids.length === 0) {
|
|
85
|
+
const text = `${package_name} is not running on ${serial} — no logs to read.`;
|
|
86
|
+
return {
|
|
87
|
+
content: [{ type: "text", text }],
|
|
88
|
+
structuredContent: {
|
|
89
|
+
device_id: serial,
|
|
90
|
+
package_name,
|
|
91
|
+
pids: [],
|
|
92
|
+
lines_returned: 0,
|
|
93
|
+
logs: "",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// `-t N` implies a snapshot of the last N lines (it's a superset of `-d`).
|
|
99
|
+
// Passing both is redundant and adb already caps output at N, so
|
|
100
|
+
// truncation is not observable from this side.
|
|
101
|
+
// `--pid=` accepts a comma-separated list on API 28+. On older devices
|
|
102
|
+
// multi-PID filtering may be ignored; most apps run as a single process.
|
|
103
|
+
const logcatArgs = ["logcat", "-t", String(lineCount), "-v", "threadtime"];
|
|
104
|
+
if (pids && pids.length > 0) {
|
|
105
|
+
logcatArgs.push(`--pid=${pids.join(",")}`);
|
|
106
|
+
}
|
|
107
|
+
if (tag) {
|
|
108
|
+
logcatArgs.push(`${tag}:${priority ?? "V"}`, "*:S");
|
|
109
|
+
}
|
|
110
|
+
else if (priority) {
|
|
111
|
+
logcatArgs.push(`*:${priority}`);
|
|
112
|
+
}
|
|
113
|
+
const { stdout } = await runAdb([...deviceArgs, ...logcatArgs], {
|
|
114
|
+
timeoutMs: LOGCAT_TIMEOUT_MS,
|
|
115
|
+
maxBufferBytes: LOGCAT_BUFFER_BYTES,
|
|
116
|
+
});
|
|
117
|
+
const trimmed = stdout.replace(/\r?\n$/, "");
|
|
118
|
+
const linesReturned = trimmed.length === 0 ? 0 : trimmed.split(/\r?\n/).length;
|
|
119
|
+
const header = [
|
|
120
|
+
`Device: ${serial}`,
|
|
121
|
+
package_name ? `Package: ${package_name} (PIDs: ${pids.join(", ")})` : null,
|
|
122
|
+
tag ? `Tag: ${tag}` : null,
|
|
123
|
+
priority ? `Priority: ≥ ${priority}` : null,
|
|
124
|
+
`Lines: ${linesReturned}`,
|
|
125
|
+
]
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.join(" · ");
|
|
128
|
+
const text = linesReturned > 0
|
|
129
|
+
? `${header}\n\n${trimmed}`
|
|
130
|
+
: `${header}\n\n(no matching log entries)`;
|
|
131
|
+
return {
|
|
132
|
+
content: [{ type: "text", text }],
|
|
133
|
+
structuredContent: {
|
|
134
|
+
device_id: serial,
|
|
135
|
+
package_name,
|
|
136
|
+
pids,
|
|
137
|
+
lines_returned: linesReturned,
|
|
138
|
+
logs: trimmed,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
return toolErrorFrom(err);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=read-logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-logs.js","sourceRoot":"","sources":["../../src/tools/read-logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { UiNode } from "../uiDump.js";
|
|
3
|
+
export interface ScreenshotWithUiInput {
|
|
4
|
+
screenshotFn: () => Promise<Buffer>;
|
|
5
|
+
dumpFn: () => Promise<UiNode[]>;
|
|
6
|
+
}
|
|
7
|
+
export interface ScreenshotWithUiResult {
|
|
8
|
+
bytes: number;
|
|
9
|
+
nodes: UiNode[];
|
|
10
|
+
node_count: number;
|
|
11
|
+
png_base64: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Capture the screen PNG and the parsed UI hierarchy from the same moment,
|
|
15
|
+
* in parallel. Halves the round-trips when a caller needs both visual and
|
|
16
|
+
* semantic data. The two captures are issued concurrently but aren't truly
|
|
17
|
+
* atomic — they may be a few ms apart, which is much tighter than two
|
|
18
|
+
* separate MCP calls would produce.
|
|
19
|
+
*/
|
|
20
|
+
export declare function captureScreenshotWithUi(input: ScreenshotWithUiInput): Promise<ScreenshotWithUiResult>;
|
|
21
|
+
export declare function registerScreenshotWithUi(server: McpServer): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fetchScreenshotPng } from "./screenshot.js";
|
|
3
|
+
import { fetchUiDump } from "../uiDump.js";
|
|
4
|
+
import { resolveDeviceArgs } from "../devices.js";
|
|
5
|
+
import { deviceIdInput } from "../schemas.js";
|
|
6
|
+
import { toolErrorFrom } from "../toolResult.js";
|
|
7
|
+
/**
|
|
8
|
+
* Capture the screen PNG and the parsed UI hierarchy from the same moment,
|
|
9
|
+
* in parallel. Halves the round-trips when a caller needs both visual and
|
|
10
|
+
* semantic data. The two captures are issued concurrently but aren't truly
|
|
11
|
+
* atomic — they may be a few ms apart, which is much tighter than two
|
|
12
|
+
* separate MCP calls would produce.
|
|
13
|
+
*/
|
|
14
|
+
export async function captureScreenshotWithUi(input) {
|
|
15
|
+
const [png, nodes] = await Promise.all([input.screenshotFn(), input.dumpFn()]);
|
|
16
|
+
return {
|
|
17
|
+
bytes: png.length,
|
|
18
|
+
nodes,
|
|
19
|
+
node_count: nodes.length,
|
|
20
|
+
png_base64: png.toString("base64"),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function registerScreenshotWithUi(server) {
|
|
24
|
+
server.registerTool("screenshot_with_ui", {
|
|
25
|
+
title: "Capture screenshot and UI hierarchy together",
|
|
26
|
+
description: "Returns the PNG and the parsed interactive UI nodes from the " +
|
|
27
|
+
"same moment, in a single MCP call. Prefer over separate " +
|
|
28
|
+
"screenshot + dump_ui when you need both visual and semantic data.",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
device_id: deviceIdInput,
|
|
31
|
+
},
|
|
32
|
+
outputSchema: {
|
|
33
|
+
device_id: z.string(),
|
|
34
|
+
bytes: z.number().int().nonnegative(),
|
|
35
|
+
node_count: z.number().int().nonnegative(),
|
|
36
|
+
},
|
|
37
|
+
annotations: {
|
|
38
|
+
readOnlyHint: true,
|
|
39
|
+
destructiveHint: false,
|
|
40
|
+
idempotentHint: false,
|
|
41
|
+
openWorldHint: false,
|
|
42
|
+
},
|
|
43
|
+
}, async ({ device_id }) => {
|
|
44
|
+
try {
|
|
45
|
+
const { args: deviceArgs, serial } = await resolveDeviceArgs(device_id);
|
|
46
|
+
const result = await captureScreenshotWithUi({
|
|
47
|
+
screenshotFn: () => fetchScreenshotPng(deviceArgs),
|
|
48
|
+
dumpFn: async () => (await fetchUiDump(deviceArgs)).nodes,
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: `Captured ${result.bytes.toLocaleString()} bytes and ${result.node_count} UI node(s) from ${serial}.`,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: "image",
|
|
58
|
+
data: result.png_base64,
|
|
59
|
+
mimeType: "image/png",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
structuredContent: {
|
|
63
|
+
device_id: serial,
|
|
64
|
+
bytes: result.bytes,
|
|
65
|
+
node_count: result.node_count,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return toolErrorFrom(err);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=screenshot-with-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot-with-ui.js","sourceRoot":"","sources":["../../src/tools/screenshot-with-ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAejD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAA4B;IAE5B,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/E,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,MAAM;QACjB,KAAK;QACL,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACnC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,8CAA8C;QACrD,WAAW,EACT,+DAA+D;YAC/D,0DAA0D;YAC1D,mEAAmE;QACrE,WAAW,EAAE;YACX,SAAS,EAAE,aAAa;SACzB;QACD,YAAY,EAAE;YACZ,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;YACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;YACrC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;SAC3C;QACD,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,KAAK;SACrB;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;YACxE,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC;gBAC3C,YAAY,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC;gBAClD,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK;aAC1D,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,YAAY,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,cAAc,MAAM,CAAC,UAAU,oBAAoB,MAAM,GAAG;qBAC5G;oBACD;wBACE,IAAI,EAAE,OAAgB;wBACtB,IAAI,EAAE,MAAM,CAAC,UAAU;wBACvB,QAAQ,EAAE,WAAW;qBACtB;iBACF;gBACD,iBAAiB,EAAE;oBACjB,SAAS,EAAE,MAAM;oBACjB,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,UAAU,EAAE,MAAM,CAAC,UAAU;iBAC9B;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Capture the device screen as a PNG buffer via `adb exec-out screencap -p`.
|
|
4
|
+
* Validates the result starts with a PNG signature. Used by both the
|
|
5
|
+
* standalone screenshot tool and the screenshot_with_ui composite.
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchScreenshotPng(deviceArgs: string[]): Promise<Buffer>;
|
|
8
|
+
export declare function registerScreenshot(server: McpServer): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve as resolvePath, sep } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { runAdbBinary } from "../adb.js";
|
|
5
|
+
import { resolveDeviceArgs } from "../devices.js";
|
|
6
|
+
import { FlingError } from "../errors.js";
|
|
7
|
+
import { deviceIdInput } from "../schemas.js";
|
|
8
|
+
import { toolErrorFrom } from "../toolResult.js";
|
|
9
|
+
const SCREENSHOT_TIMEOUT_MS = 30_000;
|
|
10
|
+
const SCREENSHOT_MAX_BYTES = 30 * 1024 * 1024;
|
|
11
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
12
|
+
function looksLikePng(buf) {
|
|
13
|
+
return buf.length >= 8 && buf.subarray(0, 8).equals(PNG_SIGNATURE);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Capture the device screen as a PNG buffer via `adb exec-out screencap -p`.
|
|
17
|
+
* Validates the result starts with a PNG signature. Used by both the
|
|
18
|
+
* standalone screenshot tool and the screenshot_with_ui composite.
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchScreenshotPng(deviceArgs) {
|
|
21
|
+
const { stdout } = await runAdbBinary([...deviceArgs, "exec-out", "screencap", "-p"], { timeoutMs: SCREENSHOT_TIMEOUT_MS, maxBufferBytes: SCREENSHOT_MAX_BYTES });
|
|
22
|
+
if (!looksLikePng(stdout)) {
|
|
23
|
+
throw new FlingError("ADB_FAILED", "screencap output did not start with a PNG signature. " +
|
|
24
|
+
"This usually means the device returned an error or the binary stream was corrupted.");
|
|
25
|
+
}
|
|
26
|
+
return stdout;
|
|
27
|
+
}
|
|
28
|
+
export function registerScreenshot(server) {
|
|
29
|
+
server.registerTool("screenshot", {
|
|
30
|
+
title: "Capture a screenshot",
|
|
31
|
+
description: "Grab the device's current screen as a PNG via `adb exec-out screencap -p`. " +
|
|
32
|
+
"Returns the image inline as MCP image content. Pass `save_to` to also write " +
|
|
33
|
+
"the PNG to disk on the host. When you also need parsed UI nodes from the same " +
|
|
34
|
+
"moment, prefer `screenshot_with_ui` (halves the round-trips).",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
device_id: deviceIdInput,
|
|
37
|
+
save_to: z
|
|
38
|
+
.string()
|
|
39
|
+
.min(1)
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Optional host path to also save the PNG (relative to MCP server cwd)."),
|
|
42
|
+
},
|
|
43
|
+
outputSchema: {
|
|
44
|
+
device_id: z.string(),
|
|
45
|
+
bytes: z.number().int().positive(),
|
|
46
|
+
saved_to: z.string().optional(),
|
|
47
|
+
},
|
|
48
|
+
annotations: {
|
|
49
|
+
readOnlyHint: true,
|
|
50
|
+
destructiveHint: false,
|
|
51
|
+
idempotentHint: false,
|
|
52
|
+
openWorldHint: false,
|
|
53
|
+
},
|
|
54
|
+
}, async ({ device_id, save_to }) => {
|
|
55
|
+
try {
|
|
56
|
+
const { args: deviceArgs, serial } = await resolveDeviceArgs(device_id);
|
|
57
|
+
const stdout = await fetchScreenshotPng(deviceArgs);
|
|
58
|
+
let savedTo;
|
|
59
|
+
if (save_to) {
|
|
60
|
+
// Constrain save_to to the server's cwd to prevent path-traversal
|
|
61
|
+
// primitives. A malicious or compromised assistant could otherwise
|
|
62
|
+
// pass `../../.ssh/authorized_keys` and overwrite arbitrary files.
|
|
63
|
+
// Users who genuinely want to write elsewhere can run the MCP server
|
|
64
|
+
// from that directory.
|
|
65
|
+
const cwd = process.cwd();
|
|
66
|
+
const resolved = resolvePath(cwd, save_to);
|
|
67
|
+
const cwdWithSep = cwd.endsWith(sep) ? cwd : cwd + sep;
|
|
68
|
+
if (resolved !== cwd && !resolved.startsWith(cwdWithSep)) {
|
|
69
|
+
throw new FlingError("INVALID_INPUT", `save_to "${save_to}" resolves to ${resolved}, which is outside the server's cwd (${cwd}).`);
|
|
70
|
+
}
|
|
71
|
+
savedTo = resolved;
|
|
72
|
+
await writeFile(savedTo, stdout);
|
|
73
|
+
}
|
|
74
|
+
const base64 = stdout.toString("base64");
|
|
75
|
+
const summaryLines = [
|
|
76
|
+
`Captured ${stdout.length.toLocaleString()} bytes from ${serial}.`,
|
|
77
|
+
];
|
|
78
|
+
if (savedTo)
|
|
79
|
+
summaryLines.push(`Saved to ${savedTo}.`);
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{ type: "text", text: summaryLines.join("\n") },
|
|
83
|
+
{ type: "image", data: base64, mimeType: "image/png" },
|
|
84
|
+
],
|
|
85
|
+
structuredContent: {
|
|
86
|
+
device_id: serial,
|
|
87
|
+
bytes: stdout.length,
|
|
88
|
+
saved_to: savedTo,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
return toolErrorFrom(err);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=screenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"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"}
|