TaskSultan Docs tasksultan.com

Capabilities

Desktop

Drive native Windows applications through ctx.desktop, addressed by accessible Name and ControlType over Windows UI Automation, with a named refusal whenever a control cannot be found.

The desktop capability drives native Windows applications. It addresses controls through Windows UI Automation, the accessibility layer behind every Windows app. It names up to four things about a control: its accessible Name, its ControlType, the application's own AutomationId and the window it sits inside.

#The locator

A DesktopLocator is the accessibility tree's own language. There is no coordinate field, no rectangle and no positional index anywhere in the type, because none of those is an identity: a coordinate survives no resize, no DPI change and no theme change.

const locator = {
  controlType: 'Button',
  name: 'Eight',
  automationId: 'num8Button',
  within: { controlType: 'Window', name: 'Calculator' },
};

within scopes the search to one top-level window. Its name is matched as a substring, because a browser puts its active tab in its own window title and the title moves. A target's name, by contrast, is matched exactly. after addresses the first element of the locator's ControlType that follows a named anchor, which is how a value that changes every run is read from the stable label beside it.

#The actions

ctx.desktop is a DesktopFacade with ten verbs:

  • engine() reports which bridge answered and whether an interactive desktop is present.
  • read(locator) returns the control's Name, ControlType and enabled flag, plus its toggle state for a checkable control.
  • press(locator) invokes the control through UIA's Invoke pattern.
  • text(locator) returns the element's accessible Name as a string, which for a display or a counter is its value.
  • setValue(locator, value) writes through UIA's Value pattern.
  • activate(locator) brings the window to the front.
  • closeWindow(locator) closes a window through the Window pattern.
  • select(locator, option) chooses a named option of a dropdown.
  • toggle(locator) flips a checkable control through the Toggle pattern.
  • selectItem(locator) selects the matched element through its own SelectionItem pattern.

The engine is the windows-uia bridge, a PowerShell-driven UIA provider. The platform never injects synthetic input. Every action is an accessibility API the application itself implements, so there is no SendKeys and no coordinate click to fall back on.

#When a control cannot be found

Every miss is a named SystemException, never a null. DesktopRefusalCode is a closed set of seven:

  • DESKTOP_UIA_UNAVAILABLE, no bridge in this runtime.
  • DESKTOP_SESSION_INTERACTIVE_REQUIRED, a bridge but no interactive desktop.
  • DESKTOP_APP_NOT_RUNNING, the desktop presents no application window at all.
  • DESKTOP_TARGET_NOT_RENDERED, the window is up but its content has not rendered yet.
  • DESKTOP_MULTIPLE_MATCHES, more than one element matches, refused rather than taking the first.
  • DESKTOP_TARGET_NOT_FOUND, nothing matches, or the locator is not a locator at all.
  • DESKTOP_VALUE_BEARING_NOT_ADDRESSABLE, the element's accessible Name is its value, so the contract refuses to locate by that name.

The runner adds two more when a facade was never supplied: DESKTOP_NOT_DECLARED and DESKTOP_UNAVAILABLE.

#The window must be active

A Windows application publishes a collapsed accessibility tree until it is the active window. Notepad and Calculator both enumerate their window and offer no content tree until brought forward, which is why a run that starts with another window in front finds none of its controls and refuses DESKTOP_TARGET_NOT_FOUND against an application that is plainly live.

Make activate the first step:

import { defineAutomation } from '../framework';

export default defineAutomation({
  id: 'calculator-eight-press',
  displayName: 'Press eight on Calculator',
  capabilities: ['desktop'],
  url: '',
  getTransaction: async () => ({ id: 'one', payload: {} }),
  processTransaction: async (ctx) => {
    await ctx.desktop.activate({ controlType: 'Window', name: 'Calculator' });
    await ctx.desktop.press({
      controlType: 'Button',
      name: 'Eight',
      within: { controlType: 'Window', name: 'Calculator' },
    });
  },
});

#Things that catch people out

Declare 'desktop'. Both the runner and the Orchestrator gate on it. A desktop run also needs a robot on an interactive session. A service, or a task set to run whether or not a user is logged on, has no desktop at all.

A control whose accessible Name is its value cannot be located by that name. Address it by the application-owned AutomationId, or by an after relation to a stable anchor, or the contract refuses by name.