@slack/radar-mcp 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/tools.js +11 -0
- package/dist/shared/android.d.ts +8 -0
- package/dist/shared/android.js +10 -0
- package/dist/shared/constants.d.ts +14 -0
- package/dist/shared/constants.js +14 -0
- package/dist/shared/db.d.ts +75 -0
- package/dist/shared/db.js +403 -0
- package/dist/shared/screen.d.ts +132 -0
- package/dist/shared/screen.js +576 -0
- package/dist/web/bin.js +20 -1
- package/dist/web/log-session.d.ts +89 -0
- package/dist/web/log-session.js +144 -0
- package/dist/web/public/index.html +321 -13
- package/dist/web/server.d.ts +13 -11
- package/dist/web/server.js +399 -48
- package/dist/web/spec.d.ts +13 -5
- package/dist/web/spec.js +23 -5
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live device-screen mirror + record, for the dashboard's Screen overlay.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline (scrcpy-lite, no custom decoder): `adb exec-out screenrecord
|
|
5
|
+
* --output-format=h264` on the active display -> `ffmpeg` transcodes h264 to
|
|
6
|
+
* MJPEG -> the server pushes JPEG frames to a browser <img> over
|
|
7
|
+
* multipart/x-mixed-replace. Record tees the same JPEG frames to a second
|
|
8
|
+
* ffmpeg (mjpeg in -> libx264 mp4), so the live view and the recording share one
|
|
9
|
+
* screenrecord (the way scrcpy shares one capture).
|
|
10
|
+
*
|
|
11
|
+
* SENSITIVE: this mirrors the real device screen, which shows live DMs, message
|
|
12
|
+
* content, and anything else on screen. It is OFF by default and gated behind an
|
|
13
|
+
* explicit per-process consent (see consent state below); the server refuses the
|
|
14
|
+
* stream and record routes until consent is granted.
|
|
15
|
+
*
|
|
16
|
+
* DEPENDENCY POLICY: ffmpeg is NOT bundled and NOT a hard dependency (it is large
|
|
17
|
+
* and license-encumbered; the published package keeps a single runtime dep). The
|
|
18
|
+
* stream/record paths detect-and-degrade: present -> embedded MJPEG; absent ->
|
|
19
|
+
* a clear message plus the platform install command, and a scrcpy fallback hint.
|
|
20
|
+
* The screenshot path uses pure `screencap` (PNG, no transcode) so it MUST keep
|
|
21
|
+
* working with no ffmpeg.
|
|
22
|
+
*
|
|
23
|
+
* Hard-won device facts baked in (Pixel 10 Pro Fold and similar):
|
|
24
|
+
* - Active-display detect must be deterministic: pick the powered-ON physical
|
|
25
|
+
* panel from `cmd display get-displays`. Byte-probing cannot tell a dozing
|
|
26
|
+
* black panel from a live one (a DOZE panel still emits ~1 keyframe).
|
|
27
|
+
* - `screencap -p` prepends a "[Warning] Multiple displays were found" text line
|
|
28
|
+
* on multi-display devices, corrupting the PNG. Strip everything before the
|
|
29
|
+
* PNG magic bytes.
|
|
30
|
+
* - screenrecord emits full-range YUV that the mjpeg encoder rejects; `-vf
|
|
31
|
+
* format=yuvj420p` fixes it.
|
|
32
|
+
* - H.264 emits nothing on a static screen (correct, not a hang). A
|
|
33
|
+
* multipart/x-mixed-replace viewer only paints part N when part N+1 arrives,
|
|
34
|
+
* so a keepalive re-pushes the last frame while idle to flush it.
|
|
35
|
+
* - adb does not reliably SIGKILL the device-side screenrecord on host pipe
|
|
36
|
+
* close; orphaned screenrecord procs congest the single adb transport. Kill
|
|
37
|
+
* explicitly on cleanup.
|
|
38
|
+
*/
|
|
39
|
+
import { type ChildProcess } from "child_process";
|
|
40
|
+
/**
|
|
41
|
+
* Remove all recorded mp4s from the screen tmp dir. Recordings capture sensitive
|
|
42
|
+
* live screen content and must not linger across sessions on disk. Called on
|
|
43
|
+
* resetScreenState() (device re-enable) and on process exit — the same posture the
|
|
44
|
+
* DB-browser uses for its pulled copies. Scoped to the radar_screen_*.mp4 names this
|
|
45
|
+
* module writes so it never deletes an unrelated file that landed in the dir.
|
|
46
|
+
*/
|
|
47
|
+
export declare function cleanupRecordings(): void;
|
|
48
|
+
/** Resolve an ffmpeg binary, or null if none is installed. Cached. */
|
|
49
|
+
export declare function resolveFfmpeg(): string | null;
|
|
50
|
+
/** Whether scrcpy is installed (the no-ffmpeg fallback hint). Cached. */
|
|
51
|
+
export declare function hasScrcpy(): boolean;
|
|
52
|
+
/** The platform-specific copy-paste command to install ffmpeg. Never run automatically. */
|
|
53
|
+
export declare function ffmpegInstallHint(): string;
|
|
54
|
+
/** Capability summary for /health: what the screen overlay can do on this machine. */
|
|
55
|
+
export declare function screenCapabilities(): {
|
|
56
|
+
screenshot: boolean;
|
|
57
|
+
liveCast: boolean;
|
|
58
|
+
ffmpeg: boolean;
|
|
59
|
+
scrcpy: boolean;
|
|
60
|
+
installHint: string | null;
|
|
61
|
+
};
|
|
62
|
+
/** Grant consent to mirror the live screen. Per server process. */
|
|
63
|
+
export declare function grantScreenConsent(): void;
|
|
64
|
+
export declare function hasScreenConsent(): boolean;
|
|
65
|
+
export interface DisplayInfo {
|
|
66
|
+
on: boolean;
|
|
67
|
+
phys: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse `adb shell cmd display get-displays` output into per-display
|
|
71
|
+
* {on, phys}, dropping any block without a physical id. The active panel is the
|
|
72
|
+
* one whose block reports state ON.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseDisplays(out: string): DisplayInfo[];
|
|
75
|
+
/** Choose the physical display id to record: the powered-ON panel, else a lone panel, else null. */
|
|
76
|
+
export declare function pickDisplay(displays: DisplayInfo[]): {
|
|
77
|
+
phys: string | null;
|
|
78
|
+
note: string;
|
|
79
|
+
};
|
|
80
|
+
/** Strip any text prefix (e.g. a multi-display warning) before the PNG magic header. */
|
|
81
|
+
export declare function stripPngPrefix(raw: Buffer): Buffer;
|
|
82
|
+
/** Detect (and cache) the active display id. resetScreenState() forces a re-detect. */
|
|
83
|
+
export declare function detectDisplay(): Promise<string | null>;
|
|
84
|
+
export declare function displayNote(): string;
|
|
85
|
+
/** Capture a full-resolution PNG of the active display. Works without ffmpeg. */
|
|
86
|
+
export declare function captureScreenshot(): Promise<Buffer | null>;
|
|
87
|
+
interface Recording {
|
|
88
|
+
proc: ChildProcess;
|
|
89
|
+
path: string;
|
|
90
|
+
}
|
|
91
|
+
interface LiveCast {
|
|
92
|
+
sr: ChildProcess;
|
|
93
|
+
ff: ChildProcess;
|
|
94
|
+
viewers: Set<NodeJS.WritableStream>;
|
|
95
|
+
lastFrame: Buffer | null;
|
|
96
|
+
lastFrameTs: number;
|
|
97
|
+
recording: Recording | null;
|
|
98
|
+
fps: number;
|
|
99
|
+
keepalive: NodeJS.Timeout | null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Start (or return the existing) live cast. Returns null if ffmpeg is unavailable
|
|
103
|
+
* — callers must degrade rather than assume a stream. The boundary string is
|
|
104
|
+
* SC_BOUNDARY; the route serves multipart/x-mixed-replace with it.
|
|
105
|
+
*/
|
|
106
|
+
export declare function startLive(fps: number): LiveCast | null;
|
|
107
|
+
/** Attach an HTTP response as a viewer of the live cast (after the multipart headers are written). */
|
|
108
|
+
export declare function addViewer(L: LiveCast, res: NodeJS.WritableStream): void;
|
|
109
|
+
export declare function removeViewer(res: NodeJS.WritableStream): void;
|
|
110
|
+
export declare function getLive(): LiveCast | null;
|
|
111
|
+
export declare function lastFrame(): Buffer | null;
|
|
112
|
+
export declare function primeIfNeeded(): Promise<void>;
|
|
113
|
+
export declare const SCREEN_BOUNDARY = "radarframe";
|
|
114
|
+
export declare function startRecording(): string | null;
|
|
115
|
+
export declare function stopRecording(L?: LiveCast): Promise<string | null>;
|
|
116
|
+
/**
|
|
117
|
+
* Resolve a recording for download. Restricted to the screen tmp dir AND to the
|
|
118
|
+
* radar_screen_*.mp4 name this module writes: path.basename strips any directory
|
|
119
|
+
* (no traversal), and the prefix/extension check means a download request can only
|
|
120
|
+
* ever name a radar recording, never an arbitrary file that happened to land in the dir.
|
|
121
|
+
*/
|
|
122
|
+
export declare function recordingPath(file: string): string | null;
|
|
123
|
+
/** Reset detection + tear down any live cast. Called on device re-enable and shutdown. */
|
|
124
|
+
export declare function resetScreenState(): void;
|
|
125
|
+
/**
|
|
126
|
+
* Kill any live cast (and orphaned device-side screenrecord) on process exit. Mirrors
|
|
127
|
+
* logcat-capture.registerCleanup(); without it, Ctrl-C on an open mirror orphans the
|
|
128
|
+
* screenrecord + ffmpeg children and leaves the device transport congested for the
|
|
129
|
+
* next session. Called from createServer().
|
|
130
|
+
*/
|
|
131
|
+
export declare function registerScreenCleanup(): void;
|
|
132
|
+
export {};
|