@logictan/dsh-desktop-agent 0.1.0 → 0.1.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/README.md CHANGED
@@ -48,8 +48,8 @@ to the `cua_driver_native__*` tools it publishes and has no driver of its own.
48
48
 
49
49
  `desktop_agent({ app, goal, windowId })` resolves one window, runs the
50
50
  observe/decide/act loop until `DONE`/`BLOCKED` or a cap is reached, and returns a
51
- structured trace. `app` accepts a bundle id, an application name, or a window
52
- title; omitting it uses the frontmost window.
51
+ structured trace. `app` accepts an application name or a window title; omitting
52
+ it uses the frontmost window.
53
53
 
54
54
  Only on-screen windows are eligible. A covered window cannot be captured or acted
55
55
  on, so the tool fails with the window named rather than screenshotting whatever
package/lib/index.js CHANGED
@@ -151,7 +151,7 @@ export function apply(ctx, config) {
151
151
  'delivery — the plugin settings control that. This tool waits for the run to finish and returns a structured ' +
152
152
  'trace of every step.',
153
153
  parameters: {
154
- app: { type: 'string', description: 'Application to drive: a bundle id, an app name, or a window title. Omit to use the frontmost window.' },
154
+ app: { type: 'string', description: 'Application to drive: an app name or a window title. Omit to use the frontmost window.' },
155
155
  goal: { type: 'string', description: 'What must be true when the task is finished.', required: true },
156
156
  windowId: { type: 'integer', description: 'Exact window id from a prior listing. Omit to resolve the window from the app argument.' },
157
157
  },
@@ -235,7 +235,12 @@ async function resolveTarget(input) {
235
235
 
236
236
  const value = await dispatch('cua_driver_native__list_windows', {}, signal);
237
237
  const payload = structured(value, 'list_windows');
238
- const windows = Array.isArray(payload.windows) ? payload.windows : [];
238
+ // A payload that parses but carries no window array is a driver contract
239
+ // failure, not an empty desktop. Flattening it to [] would report the two as
240
+ // the same sentence, and the driver is a third-party boundary whose response
241
+ // shape this plugin does not control.
242
+ if (!Array.isArray(payload.windows)) throw new Error('desktop_agent: list_windows returned no window array.');
243
+ const windows = payload.windows;
239
244
  if (windows.length === 0) throw new Error('desktop_agent: the driver listed no windows.');
240
245
 
241
246
  if (args.windowId !== undefined) {
@@ -262,20 +267,41 @@ async function resolveTarget(input) {
262
267
  }
263
268
 
264
269
  const needle = args.app.toLowerCase();
265
- const matches = onScreen.filter(
266
- (window) =>
267
- String(window.app_name ?? '').toLowerCase().includes(needle) ||
268
- String(window.title ?? '').toLowerCase().includes(needle),
269
- );
270
+ const matches = onScreen.filter((window) => matchesApp(window, needle));
270
271
  if (matches.length === 0) {
272
+ // An app that is running but has no window on screen is a different fact
273
+ // from an app that is not running, and only the second one suggests
274
+ // launching it. The windowId path already tells them apart.
275
+ if (windows.some((window) => matchesApp(window, needle))) {
276
+ throw new Error(
277
+ 'desktop_agent: "' + args.app + '" is open, but no window of it is on screen, so it cannot be captured or acted on.',
278
+ );
279
+ }
271
280
  const names = [...new Set(onScreen.map((window) => window.app_name).filter(Boolean))];
272
281
  throw new Error(
273
- 'desktop_agent: no on-screen window matches "' + args.app + '". Open windows: ' + (names.join(', ') || '(none)') + '.',
282
+ 'desktop_agent: no on-screen window matches "' + args.app + '". On-screen apps: ' + (names.join(', ') || '(none)') + '.',
274
283
  );
275
284
  }
276
285
  return targetOf(bestMatch(matches));
277
286
  }
278
287
 
288
+ /**
289
+ * Whether a window answers to an app argument.
290
+ *
291
+ * The needle is matched against the application name and the window title,
292
+ * because both are things a caller can reasonably name.
293
+ *
294
+ * @param window - a window from the driver's list.
295
+ * @param needle - the app argument, lower-cased.
296
+ * @returns whether the window matches.
297
+ */
298
+ function matchesApp(window, needle) {
299
+ return (
300
+ String(window.app_name ?? '').toLowerCase().includes(needle) ||
301
+ String(window.title ?? '').toLowerCase().includes(needle)
302
+ );
303
+ }
304
+
279
305
  /**
280
306
  * Choose the window to drive from the candidates an app argument matched.
281
307
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logictan/dsh-desktop-agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "DSH desktop agent: a native-vision decision loop that drives the host desktop through the Cua Driver tools. DSH 桌面 Agent 子插件。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",