@momasiku/dsh-pilot 0.5.8 → 0.5.9

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 CHANGED
@@ -4,10 +4,12 @@
4
4
 
5
5
  **Give DeepSeek Harness hands and eyes on your desktop.**
6
6
 
7
+ [![npm](https://img.shields.io/npm/v/@momasiku/dsh-pilot?label=npm)](https://www.npmjs.com/package/@momasiku/dsh-pilot)
8
+ [![compat](https://github.com/momasiku/dsh-pilot/actions/workflows/compat.yml/badge.svg)](https://github.com/momasiku/dsh-pilot/actions/workflows/compat.yml)
7
9
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
+
8
11
  ![platform](https://img.shields.io/badge/platform-Windows-0078d4.svg)
9
- [![npm](https://img.shields.io/npm/v/@momasiku/dsh-pilot?label=npm)](https://www.npmjs.com/package/@momasiku/dsh-pilot)
10
- ![tools](https://img.shields.io/badge/tools-screen__view%20%C2%B7%20desktop__control%20%C2%B7%20desktop__sequence-success)
12
+ ![DSH](https://img.shields.io/badge/DSH-0.1.5--rc.2%20%E2%86%92%200.2.x-5b50ed.svg)
11
13
 
12
14
  [English](#why-this-exists) · [中文说明](#中文说明)
13
15
 
package/lib/index.js CHANGED
@@ -992,7 +992,7 @@ const POINTER_ACTIONS = new Set(['click', 'doubleClick', 'rightClick', 'middleCl
992
992
  const RISKS = ['low', 'medium', 'high'];
993
993
  const RISK_ORDER = { low: 0, medium: 1, high: 2 };
994
994
  /** Step fields forwarded verbatim to the action helper. */
995
- const STEP_FIELDS = ['x', 'y', 'toX', 'toY', 'amount', 'text', 'key', 'title', 'button'];
995
+ const STEP_FIELDS = ['x', 'y', 'toX', 'toY', 'amount', 'text', 'key', 'title', 'button', 'tolerant'];
996
996
 
997
997
  const SEQUENCE_STEP_RESULT_SCHEMA = {
998
998
  type: 'object',
@@ -1281,13 +1281,17 @@ function apply(ctx, config) {
1281
1281
  type: 'string',
1282
1282
  description: 'Case-insensitive window title or process substring; required by focus, and filters windows.'
1283
1283
  },
1284
+ tolerant: {
1285
+ type: 'boolean',
1286
+ description: 'focus only: when Windows refuses to hand the window the foreground, record a note and continue instead of failing the step. Use it when the coordinates are right whichever window ends up in front.'
1287
+ },
1284
1288
  capture: {
1285
1289
  type: 'boolean',
1286
1290
  description: 'Capture a fresh frame after the action and show it in this result. Default true.'
1287
1291
  },
1288
1292
  settleMs: {
1289
1293
  type: 'integer',
1290
- description: 'Milliseconds to wait before the automatic capture, so the screen can repaint. Default 750.'
1294
+ description: 'Milliseconds to wait before the automatic capture, so the screen can repaint. Default 750. Skipped when capture is false, unless you pass it explicitly.'
1291
1295
  },
1292
1296
  forceImage: {
1293
1297
  type: 'boolean',
@@ -1338,9 +1342,15 @@ function apply(ctx, config) {
1338
1342
  const wantsCapture = args.capture !== false;
1339
1343
  if (wantsCapture) await assertImageCapableRoute(ctx, exec, 'desktop_control with capture');
1340
1344
 
1341
- const settle = Number.isSafeInteger(args.settleMs) ? args.settleMs : config.settleMs;
1345
+ // The settle exists so a frame is not taken mid-repaint. With no frame to
1346
+ // take there is nothing to wait for, and the wait is dead time: measured
1347
+ // 750 ms on every desktop_control call, capturing or not. An explicit
1348
+ // settleMs is still honoured, so a caller that wants a pause before its
1349
+ // own screen_view keeps it.
1350
+ const explicitSettle = Number.isSafeInteger(args.settleMs);
1351
+ const settle = explicitSettle ? args.settleMs : (wantsCapture ? config.settleMs : 0);
1342
1352
  const params = { action: args.action, settleMs: settle };
1343
- for (const field of ['x', 'y', 'toX', 'toY', 'amount', 'text', 'key', 'title', 'button']) {
1353
+ for (const field of ['x', 'y', 'toX', 'toY', 'amount', 'text', 'key', 'title', 'button', 'tolerant']) {
1344
1354
  if (args[field] !== undefined && args[field] !== null) params[field] = args[field];
1345
1355
  }
1346
1356
 
@@ -1396,7 +1406,7 @@ function apply(ctx, config) {
1396
1406
  steps: {
1397
1407
  type: 'array',
1398
1408
  required: true,
1399
- description: 'Ordered steps. Each object is one desktop_control action: {action, x, y, toX, toY, text, key, title, amount, button, settleMs, risk, riskNote}; action "wait" takes ms. Coordinates are physical screen pixels.'
1409
+ description: 'Ordered steps. Each object is one desktop_control action: {action, x, y, toX, toY, text, key, title, amount, button, tolerant, settleMs, risk, riskNote}; action "wait" takes ms. A focus step that Windows refuses now fails unless tolerant: true. Coordinates are physical screen pixels.'
1400
1410
  },
1401
1411
  risk: {
1402
1412
  type: 'string',
@@ -304,21 +304,31 @@ switch ($action) {
304
304
  }
305
305
  }
306
306
  'focus' {
307
+ # A refused hand-off used to be a note: the step reported ok and the batch
308
+ # carried on, so every later click and keystroke went to whatever window was
309
+ # actually in front. That is the one failure this plugin must not report as
310
+ # success, so it is an error now; tolerant keeps the old behaviour for a
311
+ # caller that knows the coordinates are right either way.
312
+ $tolerant = $false
313
+ $tolerantRaw = Get-Param 'tolerant'
314
+ if ($null -ne $tolerantRaw) { $tolerant = [bool] $tolerantRaw }
315
+ $label = ''
316
+ $target = [IntPtr]::Zero
307
317
  if ($script:TargetHwnd -ne [IntPtr]::Zero) {
308
318
  # An explicit handle is authoritative and works for a window hidden
309
319
  # to the tray, which a title search cannot reach.
310
320
  if (-not [DshInput]::IsAlive($script:TargetHwnd)) { Write-ActionError "the target window (hwnd $($script:TargetHwndText)) no longer exists" }
311
- $raised = [DshInput]::ForceForeground($script:TargetHwnd)
312
- if (-not $raised) { $notes += "Windows would not hand focus to hwnd $($script:TargetHwndText); '$([DshInput]::ForegroundTitle())' kept it." }
321
+ $target = $script:TargetHwnd
322
+ $label = "hwnd $($script:TargetHwndText)"
323
+ $null = [DshInput]::ForceForeground($target)
313
324
  Start-Sleep -Milliseconds 220
314
325
  } else {
315
326
  if ([string]::IsNullOrWhiteSpace($windowTitle)) { Write-ActionError 'focus requires a title, or an hwnd' }
316
327
  $needle = $windowTitle.ToLowerInvariant()
317
328
  # EnumWindows, not Process.MainWindowHandle: an Electron, UWP or tray
318
329
  # window frequently reports no main window handle at all, so a
319
- # MainWindowHandle scan cannot find a window that `windows` (which
320
- # already enumerates) happily lists.
321
- $target = [IntPtr]::Zero
330
+ # MainWindowHandle scan cannot find a window that the windows action
331
+ # (which already enumerates) happily lists.
322
332
  $fallback = [IntPtr]::Zero
323
333
  foreach ($handle in [DshInput]::TopLevelWindows()) {
324
334
  if (-not [DshInput]::IsWindowVisible($handle)) { continue }
@@ -335,10 +345,22 @@ switch ($action) {
335
345
  }
336
346
  if ($target -eq [IntPtr]::Zero) { $target = $fallback }
337
347
  if ($target -eq [IntPtr]::Zero) { Write-ActionError "no visible top-level window matches '$windowTitle'; pass an hwnd if the window is hidden to the tray" }
338
- $raised = [DshInput]::Activate($target)
339
- if (-not $raised) { $notes += "Windows refused the foreground request for '$([DshInput]::WindowTitle($target))'; the taskbar entry may still need a click." }
348
+ $label = "'" + [DshInput]::WindowTitle($target) + "'"
349
+ $null = [DshInput]::Activate($target)
340
350
  Start-Sleep -Milliseconds 220
341
351
  }
352
+
353
+ # Ask the desktop, not the API. SetForegroundWindow is documented to be
354
+ # unreliable about its return value, and the state that decides where the
355
+ # next click lands is the foreground window, not what the call returned.
356
+ if (-not [DshInput]::IsForeground($target)) {
357
+ $holder = [DshInput]::ForegroundTitle()
358
+ if ($tolerant) {
359
+ $notes += "Windows would not hand focus to $label; '$holder' kept it (tolerant: continuing)."
360
+ } else {
361
+ Write-ActionError "could not bring $label to the foreground; '$holder' still holds it, so the rest of this batch would land there. Pass tolerant: true on this step if the coordinates are right either way."
362
+ }
363
+ }
342
364
  }
343
365
  'windows' {
344
366
  $query = $windowTitle.ToLowerInvariant()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momasiku/dsh-pilot",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "description": "Desktop vision and control for DeepSeek Harness - the whole Windows machine, any application, not browser automation: screen_view captures the live screen as an image the model can see, desktop_control drives the mouse, keyboard, and windows against the exact same pixel space; desktop_sequence runs a whole batch of actions in one call and looks once at the end.",
5
5
  "license": "MIT",
6
6
  "author": "momasiku",