Skip to main content

Configuration

spart-desk.config.ts is one file at the project root with the defaults every script and test shares. npx spart-desk init writes it.

import { defineConfig } from "@spartaficial/spart-desk";

export default defineConfig({
runtime: "local",
timeout: 10_000,
expect: { timeout: 15_000, toPass: { timeout: 30_000, intervals: [250, 500, 1_000] } },
intervals: [100, 250, 500, 1000],
diagnostics: { directory: "test-results/spart-desk" },
ocr: { languages: ["en-GB"] },
images: { directory: "tests/images" },
});

Who reads it​

ReaderWhat it takes from the file
Desktop.create(options)everything. Options given in code win; expect, resolution, ocr, images, grounding (experimental) and diagnostics merge one level deep, so Desktop.create({ expect: { timeout: 1_000 } }) keeps the file's expect.toPass.
expectDesk.poll / expectDesk(callback).toPassexpect.timeout, intervals and expect.toPass as defaults when neither the call nor expectDesk.configure (its toPass, then its timeout/intervals) set them. A desktop's own, those given to Desktop.create({ expect }) included, reach them through expectDesk.configure({ ...desktop.config.expect, intervals: desktop.config.intervals })
spart-desk-mcp / spart-desk-cli hostsas Desktop.create; the runtime stays what --runtime says (agentic-mcp.md)

Discovery​

  1. Desktop.create({ configFile }): a path, or false to read nothing.
  2. SPART_DESK_CONFIG=<path>, relative to the working directory.
  3. The first of spart-desk.config.ts, spart-desk.config.mts, spart-desk.config.js, spart-desk.config.mjs in the working directory.

Nothing found means the built-in defaults. The file is an ES module loaded with a plain import(): Node 24 strips the TypeScript types itself, so there is no build step and no extra dependency. Only erasable syntax works in it (no enum, namespace or constructor parameter properties). A file is evaluated once per process, like any module.

The file is an ES module whatever type the project's package.json declares. On its own, Node reads a .ts or .js file as CommonJS unless the nearest package.json says "type": "module", so SPART-Desk imports spart-desk.config.ts and .js through a module hook scoped to that one file, which declares it an ES module before Node parses it (Node still strips the types). import/export default therefore work in a "type": "commonjs" package and in one without a type field, without renaming the file or changing the package; .mts/.mjs are accepted too. CommonJS idioms (require, module.exports, __dirname) are not available in it: use import, export default and import.meta.dirname. ConfigError says so when one is used.

Settings​

SettingDefaultMeaning
runtime"local""local", "sandbox" (experimental, runtime-windows-sandbox.md) or a DesktopRuntime instance
timeout5000budget of auto-waiting actions, in milliseconds
expect.timeouttimeoutbudget of locator assertions, poll and toPass
expect.toPass.timeoutexpect.timeoutbudget of toPass
expect.toPass.intervalsintervalspauses between toPass attempts
expect.snapshots.directory<test file>-snapshots beside each testwhere toHaveScreenshot and toMatchTreeSnapshot keep their golden files; relative to the configuration file
expect.snapshots.updatemissing (none when CI is set)which golden files are written: missing, all or none; SPART_DESK_UPDATE_SNAPSHOTS overrides it
intervals[100, 250, 500, 1000]pauses between two observations of the UI while waiting (actions, waitFor, assertions, poll); the last entry repeats. Every auto-waiting call accepts its own intervals.
resolution{ screen: { interval: 1000, upscale: 2, parkPointer: true } }how often the screen is read while a locator waits, how small text is enlarged before it is read, and whether the mouse a click left resting on a window is taken off it before its picture is read (parkPointer). Which resolver answers a locator is the locator's own choice: the accessibility tree, unless a step names the screen (read(), getByOcr, getByImage, getByVision (experimental); resolution.md)
ocrthe recognizer built into Windows{ enabled?, languages?, minimumConfidence?, provider?, paddle? }. languages are tags to read in, best first; paddle is what the reader named "paddle" is made with ({ model?, characters?, threads?, maxSide?, batch?, runtime? }); it reads PP-OCRv6's languages, English among them, whatever languages says. provider is a reader (e.g. createPaddleOcrProvider() from @spartaficial/spart-desk-ocr-paddle), a reader's name ("windows", or "paddle" with that package installed), or a list of either tried in order: provider: ["windows", "paddle"]. A reader handed in as an object is the caller's: dispose() leaves it running, so one reader serves several desktops, and the caller disposes it once; a reader made from a name, and the built-in one, go with the desktop. Anything else, such as a misspelt name, is a ConfigError naming the file when it loads (resolution.md)
imagesnone{ directory?, threshold?, scale?, interval? } for getByImage; directory is relative to the configuration file, and interval (250 ms) is how often a picture is looked for while a locator waits. Studio's Pictures tool saves into it, as <app>/<screen>/<name>.png, with the typed index (index.mjs, index.d.mts) beside the pictures (studio.md); npx spart-desk images writes the index for pictures added by hand
grounding (experimental)none{ minimumConfidence? } in the file; provider in code (resolution.md)
diagnosticsscreenshot and UI tree on failure, in <tmpdir>/spart-desk; no trace file; no step pictures; no trace keptdirectory, traceFile and traceDirectory are relative to the config file; trace: "on" | "retain-on-failure" keeps a trace of the run as one trace.zip in traceDirectory (default test-results/spart-desk), each step with its pictures, tree and waits, and puts trace-viewer.html, the page that opens them, at the top of traceDirectory; traceFile appends every trace entry to a JSON-lines file as it happens; steps: "on" | "on-failure" pictures the window after each step beside it, which spart-desk report puts in an HTML report; redact: true | { patterns } tokenises the data texts and paints over their places in everything written (the trace file, the failure tree and picture, the step pictures, the report), as a support bundle is redacted
foreground"activate"what an action that needs real input does when its window is not in front and no way in the background is left: "activate" brings the window to the front, "require" fails with foreground_required (input-engine.md)

An unknown setting or a default export that is not an object raises ConfigError (its file names the file). Numeric ranges are checked when the desktop is created (RangeError), as for options given in code.

The engine reads one environment variable for where it keeps files: SPART_DESK_CACHE_DIR, the folder the Windows Sandbox runtime (experimental) stages the guest's files in (%LOCALAPPDATA%\spart-desk by default, runtime-windows-sandbox.md). SPART_DESK_CONFIG names the configuration file (Discovery).

Precedence​

  • Engine: options given in code > configuration file > defaults. --config <file> names the file for the spart-desk command; --no-config ignores it.
  • expectDesk: per-call options > expectDesk(subject, { … }) > expectDesk.configure({ … }) (its toPass first, for toPass) > the desktop's expect (locator assertions) or the configuration file (poll and toPass, which take no locator; a desktop's own reach them through expectDesk.configure({ ...desktop.config.expect, intervals: desktop.config.intervals }))

    defaults.

npx spart-desk init​

npx spart-desk init
npx spart-desk init --yes # every default, no questions
npx spart-desk init --js --runtime sandbox --tests e2e # the sandbox runtime is experimental

Run in a terminal it asks, in this order: TypeScript or JavaScript (TypeScript), local or sandbox runtime (local; the sandbox runtime is experimental, see runtime-windows-sandbox.md) and where the example test goes (tests; none skips it). Nothing has to be installed: the Windows backend ships inside the package. Without a terminal, or with --yes, the defaults apply; --js, --runtime and --tests/--no-tests answer a question ahead of time.

It writes spart-desk.config.ts (spart-desk.config.mjs with --js), <tests>/example.test.ts for Vitest when the project depends on it and for node:test otherwise, and adds test-results/ to .gitignore. A node:test example is example.test.mts unless the package.json has "type": "module", because node --test reads that file itself (the configuration file has no such constraint, see Discovery). Existing files are left alone unless --force.

Programmatic access​

import { defineConfig, findConfigFile, loadConfig } from "@spartaficial/spart-desk";

const loaded = await loadConfig({ cwd: process.cwd() }); // { path, config } or undefined

loadConfig({ cwd?, file?, env? }) applies the discovery above and validates the file; findConfigFile only locates it. SpartDeskConfig is the type of the default export (DesktopOptions without configFile).