davidekingsss/dsh-screen-eye ↗★ 0
dsh-screen-eye
让AI在单次调用中自主截取并读取当前屏幕画面 适合需要AI具备屏幕视觉能力、自动捕获并分析macOS/Windows屏幕的任务。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:davidekingsss/dsh-screen-eye說明文件
閱讀完整 README ↗Configuration
All keys are optional — and all of them are editable from the harness's own
settings page, Settings → Screen Eye, or by hand in ~/.dsh/settings.yaml
under screen-eye:. Either way an edit reaches the next call without a restart,
and the plugin's mount entry stays the base that a cleared field falls back to.
docs/settings.md has the three layers, the page, and the
two platform constraints behind it.
| Key | Default | Meaning |
|---|---|---|
outputDir | ` | |
| /Screen Eye` | Where captured PNGs are written — the system pictures folder, in a folder of its own so fifty screenshots do not land among the user's photographs. | |
locale | en | Language of the onboarding text: en or zh. |
timeoutMs | 300000 | Budget for one whole call, including any wait_for_change. The capture gets what the wait did not spend. |
frames / interval_ms | 1 / 200 | Frames per call and the target gap between them. At most 600, which is the provider's per-request image limit rather than a policy here — past it the extra images would be taken and then replaced with a placeholder. Each frame is one image and at most 384 vision tokens (about 380 measured), so the count is a cost decision and it is yours. |
duration_ms | — | State how long the motion lasts and let the tool size the burst, instead of computing frames and interval yourself. |
frames, interval_ms and duration_ms describe one burst and any two of
them determine the third: frames with an interval gives the span, frames with
a duration gives the interval that divides it, an interval with a duration gives
how many frames fit. All three at once is refused rather than resolved.
That is also the cost lever. Each frame is an image and images are what cost, so
holding the window fixed and raising interval_ms trades resolution for
cost — the same motion watched with fewer images — while lowering it spends more
for a finer sample. A window given alone is sampled at the ordinary frame count,
not at the maximum, because the maximum is the most expensive answer and was not
asked for — unless the call is waiting for a change first, where the ending is
the screen's to decide and the count is headroom.
wait_for_change: true also settles when the burst ends: it stops once the
picture stops changing, so frames is an upper bound and the reply says which
ending happened. wait_timeout_ms (default 30000) is how long it waits for the
motion to start. until_still: false opts out, recording the full window
instead — the right call for a span rather than an event.
| maxDimension | 4096 | Largest side, in pixels, a capture may have. 4096 is where the provider's per-image side limit lands once a request carries fifteen or more images, and a burst can carry hundreds. A capture over the cap is refused with its size named rather than resized — so a display larger than this needs the field raised to be captured whole. |
| keepRecent | 50 | How many of the newest captures to keep in outputDir. A capture is a few-megabyte PNG and an agent using its eyes takes many, so the directory is bounded by default. 0 keeps everything. |
| requireImageCapableModel | true | Refuse a capture when the calling model declares no image input, instead of returning a picture it cannot see. |
| deleteAfterCommit | false | Delete the PNG once it is committed to the attachment store. Off by default, so the returned path stays re-readable. |
Captures are committed without the records that would disqualify them from
lossless storage. On macOS that means stripping the ICC profile, EXIF and iTXt
records screencapture attaches to every shot, while the file on disk keeps
them; those records decide whether the attachment store keeps the bytes or
re-encodes them, so removing them is what lets a capture be stored as written
instead of as WebP at quality 85. On Windows there is nothing to strip: GDI+
writes none of them, so a capture already satisfies the store's condition. The
store converts to sRGB either way, so nothing that survives is lost, and the
file you can open keeps its colour profile.
Retention only ever removes files this plugin wrote: regular files, direct
children of outputDir, whose names match the exact shape it generates
(shot--.png). It is never recursive, it never touches
another naming scheme, and it never removes the capture it just returned.