automify 0.1.10 → 0.2.0
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 +78 -12
- package/examples/browser-basic.js +4 -1
- package/examples/desktop-local.js +1 -1
- package/package.json +1 -1
- package/scripts/install-desktop.js +15 -1
- package/src/index.d.ts +7 -1
- package/src/lib/argument-reference.js +3 -3
- package/src/lib/automify.js +1 -1
- package/src/lib/cli-automify.js +1 -1
- package/src/lib/desktop-runtime.js +8 -1
- package/src/lib/local-desktop-computer.js +1 -0
package/README.md
CHANGED
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
`Automify` is a Node.js library for AI computer use and command use across web apps, terminals, native desktops, Docker CLI sandboxes, and Docker-backed Linux desktops.
|
|
10
10
|
|
|
11
|
+
Created by [Aldo Vincenti](https://aldovincenti.com).
|
|
12
|
+
|
|
11
13
|
Computer use surfaces:
|
|
12
14
|
|
|
13
|
-
| Surface | Factory | Controlled environment
|
|
14
|
-
| -------------- | --------------------------- |
|
|
15
|
-
| Browser | `automify.browser()` | Playwright browser with screenshots and actions
|
|
16
|
-
| Desktop | `automify.localComputer()` | Native desktop on
|
|
17
|
-
| Docker desktop | `automify.dockerComputer()` | Linux desktop inside a running Docker container
|
|
15
|
+
| Surface | Factory | Controlled environment |
|
|
16
|
+
| -------------- | --------------------------- | --------------------------------------------------------- |
|
|
17
|
+
| Browser | `automify.browser()` | Playwright browser with screenshots and actions |
|
|
18
|
+
| Desktop | `automify.localComputer()` | Native desktop on macOS, Windows, or Linux X11/Xorg hosts |
|
|
19
|
+
| Docker desktop | `automify.dockerComputer()` | Linux desktop inside a running Docker container |
|
|
18
20
|
|
|
19
21
|
Command use surfaces:
|
|
20
22
|
|
|
@@ -40,6 +42,9 @@ Full docs live at [aldovincenti.github.io/automify](https://aldovincenti.github.
|
|
|
40
42
|
|
|
41
43
|
```bash
|
|
42
44
|
npm install automify
|
|
45
|
+
|
|
46
|
+
# Ubuntu 26.04 only, if Playwright blocks Chromium install
|
|
47
|
+
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=ubuntu24.04-x64 npm install automify
|
|
43
48
|
```
|
|
44
49
|
|
|
45
50
|
Chromium is installed by the package `postinstall` script. Skip it with:
|
|
@@ -66,6 +71,37 @@ npm install zod
|
|
|
66
71
|
|
|
67
72
|
Automify does not require Zod for `jsonOutput()` or any browser, CLI, or desktop runtime.
|
|
68
73
|
|
|
74
|
+
### Optional Docker Setup
|
|
75
|
+
|
|
76
|
+
Docker is required only for `automify.dockerCli()` and `automify.dockerComputer()`.
|
|
77
|
+
|
|
78
|
+
On macOS and Windows, install Docker Desktop from the official Docker website:
|
|
79
|
+
|
|
80
|
+
- macOS: <https://docs.docker.com/desktop/setup/install/mac-install/>
|
|
81
|
+
- Windows: <https://docs.docker.com/desktop/setup/install/windows-install/>
|
|
82
|
+
|
|
83
|
+
On Ubuntu, install Docker from the Ubuntu repositories:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
sudo apt-get update
|
|
87
|
+
sudo apt-get install -y docker.io
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Use `docker.io`, not the `docker` package. In Ubuntu packages, `docker.io` provides the Docker Engine/runtime and CLI.
|
|
91
|
+
|
|
92
|
+
Start Docker on Ubuntu and enable it after reboot:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
sudo systemctl enable --now docker
|
|
96
|
+
sudo docker run hello-world
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
To run Docker commands without `sudo`, add your user to the `docker` group, then log out and back in:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
sudo usermod -aG docker $USER
|
|
103
|
+
```
|
|
104
|
+
|
|
69
105
|
## Quick Start
|
|
70
106
|
|
|
71
107
|
```js
|
|
@@ -99,7 +135,10 @@ try {
|
|
|
99
135
|
})
|
|
100
136
|
});
|
|
101
137
|
|
|
102
|
-
console.log(run.
|
|
138
|
+
console.log(run.parsed.id, run.parsed.firstName, run.parsed.lastName);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("Automation failed:", error);
|
|
141
|
+
process.exitCode = 1;
|
|
103
142
|
} finally {
|
|
104
143
|
await browser.close();
|
|
105
144
|
}
|
|
@@ -147,7 +186,7 @@ const cli = automify.cli({
|
|
|
147
186
|
await cli.do("Run the tests and summarize failures");
|
|
148
187
|
```
|
|
149
188
|
|
|
150
|
-
Use Docker CLI when command execution should happen inside an isolated container. Docker must be installed and running before you create the adapter:
|
|
189
|
+
Use Docker CLI when command execution should happen inside an isolated container. Docker must be installed and running before you create the adapter. See [Optional Docker Setup](#optional-docker-setup) if you still need to install Docker:
|
|
151
190
|
|
|
152
191
|
```js
|
|
153
192
|
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
@@ -198,7 +237,9 @@ try {
|
|
|
198
237
|
|
|
199
238
|
### Desktop Computer Use
|
|
200
239
|
|
|
201
|
-
Local desktop computer use controls the native desktop on the machine running your Node.js process. It supports macOS, Windows, and Linux through the local desktop adapter. It needs native desktop dependencies that are not installed by default, and your OS may ask for permission to control the desktop.
|
|
240
|
+
Local desktop computer use controls the native desktop on the machine running your Node.js process. It supports macOS, Windows, and Linux through the local desktop adapter. On Linux, local desktop support requires X11/Xorg or Xvfb; Wayland sessions are not supported. It needs native desktop dependencies that are not installed by default, and your OS may ask for permission to control the desktop.
|
|
241
|
+
|
|
242
|
+
**Linux Wayland is not supported for local desktop control.** If `echo $XDG_SESSION_TYPE` prints `wayland`, `localComputer()` can fail during screenshot capture with native X11 errors such as `BadMatch` / `X_GetImage`. Use an Xorg session, run under Xvfb with `forceVirtualDisplay`, or use `dockerComputer()` for an isolated Linux desktop.
|
|
202
243
|
|
|
203
244
|
Before running `npx automify-install-desktop`, install the native build tools for your OS:
|
|
204
245
|
|
|
@@ -224,9 +265,9 @@ sudo dnf install -y gcc-c++ make cmake libXtst-devel libpng-devel
|
|
|
224
265
|
sudo pacman -S --needed base-devel cmake libxtst libpng
|
|
225
266
|
```
|
|
226
267
|
|
|
227
|
-
On Linux, install the full package list before running `npx automify-install-desktop`; the installer checks for command-line build tools but does not verify every native library package. On headless Linux hosts, also install `xvfb` unless you manage `DISPLAY` yourself. On macOS, install Homebrew first if `brew` is not available, then install CMake with `brew install cmake`. On macOS and Windows, `cmake --version` must work in the terminal where you run `npx automify-install-desktop`. On Windows, the VS Code CMake Tools extension is not enough by itself, and Visual Studio 2026 is not currently recognized by the native build chain used by nut.js.
|
|
268
|
+
On Linux, install the full package list before running `npx automify-install-desktop`; the installer checks for command-line build tools but does not verify every native library package. Linux local desktop capture is X11-based: use Xorg/X11, not Wayland. On headless Linux hosts, also install `xvfb` unless you manage `DISPLAY` yourself. On macOS, install Homebrew first if `brew` is not available, then install CMake with `brew install cmake`. On macOS and Windows, `cmake --version` must work in the terminal where you run `npx automify-install-desktop`. On Windows, the VS Code CMake Tools extension is not enough by itself, and Visual Studio 2026 is not currently recognized by the native build chain used by nut.js.
|
|
228
269
|
|
|
229
|
-
`npx automify-install-desktop` stores the compiled desktop runtime outside `node_modules` in a long-term cache, so normal `npm update` runs do not remove it. If a later `npm install` or `npm update` detects that a previously installed desktop runtime no longer matches the current
|
|
270
|
+
`npx automify-install-desktop` stores the compiled desktop runtime outside `node_modules` in a long-term cache, so normal `npm update` runs do not remove it. If the command is run again and the cached runtime already matches the current platform, CPU architecture, Node ABI, and pinned nut.js/libnut revisions, Automify prints a skip message and exits without rebuilding. Use `npx automify-install-desktop --force` (or `npx automify-install-desktop force`) to rebuild a compatible cache anyway. If a later `npm install` or `npm update` detects that a previously installed desktop runtime no longer matches the current environment, Automify rebuilds it automatically during `postinstall`. Default cache roots are `%LOCALAPPDATA%\automify\desktop-runtime` on Windows, `~/Library/Caches/automify/desktop-runtime` on macOS, and `${XDG_CACHE_HOME:-~/.cache}/automify/desktop-runtime` on Linux. Override with `AUTOMIFY_DESKTOP_RUNTIME_DIR`; disable auto-rebuild with `AUTOMIFY_SKIP_DESKTOP_AUTO_REBUILD=1`.
|
|
230
271
|
|
|
231
272
|
```js
|
|
232
273
|
import { initAutomify } from "automify";
|
|
@@ -244,14 +285,14 @@ const desktop = await automify.localComputer();
|
|
|
244
285
|
|
|
245
286
|
try {
|
|
246
287
|
await desktop.do(
|
|
247
|
-
"Open the Calendar app installed on this computer, find the next event
|
|
288
|
+
"Open the Calendar app installed on this computer, find the next event, and summarize it. Do not create or edit events."
|
|
248
289
|
);
|
|
249
290
|
} finally {
|
|
250
291
|
await desktop.close();
|
|
251
292
|
}
|
|
252
293
|
```
|
|
253
294
|
|
|
254
|
-
For isolated Linux desktop computer use, use Docker. `dockerComputer()` can run from a macOS, Windows, or Linux host with Docker installed and running, but the desktop it controls inside the container is Linux. Docker desktop does not use `automify-install-desktop`; it needs a running Docker daemon and an initial app command:
|
|
295
|
+
For isolated Linux desktop computer use, use Docker. `dockerComputer()` can run from a macOS, Windows, or Linux host with Docker installed and running, but the desktop it controls inside the container is Linux. This is the recommended path when the host Linux session uses Wayland, because `localComputer()` does not support Wayland. Docker desktop does not use `automify-install-desktop`; it needs a running Docker daemon and an initial app command. See [Optional Docker Setup](#optional-docker-setup) if Docker is not installed yet:
|
|
255
296
|
|
|
256
297
|
```js
|
|
257
298
|
import { initAutomify } from "automify";
|
|
@@ -323,6 +364,27 @@ const run = await browser.do("Create the lead from data and return the saved rec
|
|
|
323
364
|
- `evaluate` sends images or text files directly to the model.
|
|
324
365
|
- `shared` and `sharedFiles` expose files inside Docker CLI or Docker desktop runs.
|
|
325
366
|
- `jsonOutput()` requests structured JSON and makes parsed output available as `run.parsed`.
|
|
367
|
+
- `limits.steps` controls the maximum model-action turns before `MaxStepsExceededError`. The default is `100`.
|
|
368
|
+
|
|
369
|
+
Set max steps on an adapter when most runs need the same limit:
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
const browser = await automify.browser({
|
|
373
|
+
startUrl: "https://example.com",
|
|
374
|
+
limits: { steps: 250 }
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Override it for one `.do()` call when a task needs a different limit:
|
|
379
|
+
|
|
380
|
+
```js
|
|
381
|
+
await browser.do("Quick smoke test", {
|
|
382
|
+
limits: { steps: 25 }
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
The older flat option also works: `maxSteps: 250` is equivalent to `limits: { steps: 250 }`. If both are provided,
|
|
387
|
+
`maxSteps` wins.
|
|
326
388
|
|
|
327
389
|
For arrays of objects, the most ergonomic shape is usually an object with a named array property:
|
|
328
390
|
|
|
@@ -524,3 +586,7 @@ node --test test/e2e/live-openai.e2e.test.js
|
|
|
524
586
|
## License
|
|
525
587
|
|
|
526
588
|
MIT
|
|
589
|
+
|
|
590
|
+
## Disclaimer
|
|
591
|
+
|
|
592
|
+
Automify is distributed "as is", without warranty of any kind. Automation can control browsers, shells, desktops, files, and external services; you are responsible for how you configure and run it, and for any events associated with that use. To the maximum extent permitted by law, the author is not liable for losses, damages, data loss, service disruption, or other consequences arising from use of the software.
|
|
@@ -17,7 +17,7 @@ const desktop = automify.computer({
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
const instruction =
|
|
20
|
-
"Open the Calendar app installed on this computer, find the next event
|
|
20
|
+
"Open the Calendar app installed on this computer, find the next event, and summarize it. Do not create or edit events.";
|
|
21
21
|
|
|
22
22
|
await desktop.do(instruction, {
|
|
23
23
|
screenshots: {
|
package/package.json
CHANGED
|
@@ -7,10 +7,12 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import {
|
|
8
8
|
DESKTOP_RUNTIME_MANIFEST,
|
|
9
9
|
desktopRuntimeDir,
|
|
10
|
+
desktopRuntimeIsInstalled,
|
|
10
11
|
desktopRuntimeKey,
|
|
11
12
|
desktopRuntimeManifest,
|
|
12
13
|
desktopRuntimeNodeModules,
|
|
13
|
-
desktopRuntimeRefs
|
|
14
|
+
desktopRuntimeRefs,
|
|
15
|
+
resetDesktopRuntimeInstallState
|
|
14
16
|
} from "../src/lib/desktop-runtime.js";
|
|
15
17
|
|
|
16
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -29,10 +31,21 @@ const nutScope = join(nodeModules, "@nut-tree");
|
|
|
29
31
|
const platformPackageName = `@nut-tree/libnut-${process.platform}`;
|
|
30
32
|
const platformPackageDir = join(nutScope, `libnut-${process.platform}`);
|
|
31
33
|
const macPermissionsPackageDir = join(nutScope, "node-mac-permissions");
|
|
34
|
+
const forceInstall = process.argv.slice(2).some((arg) => arg === "--force" || arg === "force");
|
|
32
35
|
|
|
33
36
|
const runtimeDependencies = ["jimp@1.6.1", "node-abort-controller@3.1.1", "clipboardy@2.3.0", "bindings@1.5.0"];
|
|
34
37
|
|
|
38
|
+
if (!forceInstall && desktopRuntimeIsInstalled()) {
|
|
39
|
+
console.log("Automify desktop runtime is already installed and compatible; skipping rebuild.");
|
|
40
|
+
console.log(`Runtime directory: ${runtimeDir}`);
|
|
41
|
+
console.log("Use `npx automify-install-desktop --force` to rebuild it anyway.");
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
console.log("Building official nut.js from source.");
|
|
46
|
+
if (forceInstall) {
|
|
47
|
+
console.log("Force enabled: rebuilding the desktop runtime even if the cache is compatible.");
|
|
48
|
+
}
|
|
36
49
|
console.log(`Build directory: ${buildRoot}`);
|
|
37
50
|
console.log(`Runtime directory: ${runtimeDir}`);
|
|
38
51
|
console.log(`Runtime key: ${desktopRuntimeKey()}`);
|
|
@@ -46,6 +59,7 @@ checkBuildPrerequisites();
|
|
|
46
59
|
|
|
47
60
|
mkdirSync(buildRoot, { recursive: true });
|
|
48
61
|
mkdirSync(runtimeDir, { recursive: true });
|
|
62
|
+
resetDesktopRuntimeInstallState();
|
|
49
63
|
writeRuntimePackageJson();
|
|
50
64
|
cloneOrPull("https://github.com/nut-tree/libnut-core.git", libnutSource, refs.libnutCore);
|
|
51
65
|
cloneOrPull("https://github.com/nut-tree/nut.js.git", nutSource, refs.nut);
|
package/src/index.d.ts
CHANGED
|
@@ -211,7 +211,8 @@ export interface LocalDesktopComputerOptions {
|
|
|
211
211
|
};
|
|
212
212
|
/**
|
|
213
213
|
* Linux only. Defaults to true when DISPLAY is missing. Starts Xvfb so the
|
|
214
|
-
* local nut.js desktop adapter can run on headless servers.
|
|
214
|
+
* local nut.js desktop adapter can run on headless servers. Linux local
|
|
215
|
+
* desktop capture is X11-based; Wayland sessions are not supported.
|
|
215
216
|
*/
|
|
216
217
|
virtualDisplay?:
|
|
217
218
|
| boolean
|
|
@@ -224,6 +225,11 @@ export interface LocalDesktopComputerOptions {
|
|
|
224
225
|
args?: string[];
|
|
225
226
|
startupMs?: number;
|
|
226
227
|
};
|
|
228
|
+
/**
|
|
229
|
+
* Linux only. Forces Xvfb even when DISPLAY is already set. Ignored on macOS
|
|
230
|
+
* and Windows. Use this when the host Linux session is Wayland or otherwise
|
|
231
|
+
* unsuitable for X11 screenshot capture.
|
|
232
|
+
*/
|
|
227
233
|
forceVirtualDisplay?: boolean;
|
|
228
234
|
display?:
|
|
229
235
|
| string
|
|
@@ -14,7 +14,7 @@ export const argumentReference = [
|
|
|
14
14
|
"command"
|
|
15
15
|
],
|
|
16
16
|
notes:
|
|
17
|
-
"Use data for structured JSON, evaluate for files the model should inspect directly, and command only on CLI surfaces."
|
|
17
|
+
"Use data for structured JSON, evaluate for files the model should inspect directly, limits.steps to change the max model-action turns, and command only on CLI surfaces."
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
surface: "automify.browser()",
|
|
@@ -72,13 +72,13 @@ export const argumentReference = [
|
|
|
72
72
|
surface: "automify.localComputer()",
|
|
73
73
|
preferred: ["viewport", "mouse", "keyboard", "calibration", "virtualDisplay", "logFile"],
|
|
74
74
|
notes:
|
|
75
|
-
"Creates a local desktop runner and takes an exclusive cross-process lock until close(). Use logFile to capture automation and local desktop events."
|
|
75
|
+
"Creates a local desktop runner and takes an exclusive cross-process lock until close(). Linux local desktop requires X11/Xorg or Xvfb; Wayland is not supported. Use logFile to capture automation and local desktop events."
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
surface: "createLocalDesktopComputer()",
|
|
79
79
|
preferred: ["viewport", "mouse", "keyboard", "calibration", "virtualDisplay", "logFile"],
|
|
80
80
|
notes:
|
|
81
|
-
"Grouped mouse, keyboard, and calibration options are preferred over the older flat names. Use logFile to capture local desktop events. Local desktop control takes an exclusive cross-process lock until close()."
|
|
81
|
+
"Grouped mouse, keyboard, and calibration options are preferred over the older flat names. Linux local desktop requires X11/Xorg or Xvfb; Wayland is not supported. Use logFile to capture local desktop events. Local desktop control takes an exclusive cross-process lock until close()."
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
surface: "createDockerDesktopComputer()",
|
package/src/lib/automify.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
writeDebugLogFile
|
|
18
18
|
} from "./runtime.js";
|
|
19
19
|
|
|
20
|
-
const DEFAULT_MAX_STEPS =
|
|
20
|
+
const DEFAULT_MAX_STEPS = 100;
|
|
21
21
|
const DEFAULT_SCREENSHOT_DETAIL = "auto";
|
|
22
22
|
const DEFAULT_SCREENSHOT_MAX_WIDTH = 1440;
|
|
23
23
|
const DEFAULT_SCREENSHOT_MAX_HEIGHT = 1440;
|
package/src/lib/cli-automify.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
|
|
@@ -79,6 +79,13 @@ export function desktopRuntimeManifest(env = process.env) {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export function resetDesktopRuntimeInstallState(env = process.env) {
|
|
83
|
+
const runtimeDir = desktopRuntimeDir(env);
|
|
84
|
+
rmSync(join(desktopRuntimeNodeModules(env), "@nut-tree"), { recursive: true, force: true });
|
|
85
|
+
rmSync(join(runtimeDir, "package-lock.json"), { recursive: true, force: true });
|
|
86
|
+
rmSync(join(runtimeDir, "npm-shrinkwrap.json"), { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
export function readDesktopRuntimeManifest(env = process.env) {
|
|
83
90
|
const path = desktopRuntimeManifestPath(env);
|
|
84
91
|
if (!existsSync(path)) return null;
|
|
@@ -136,6 +136,7 @@ const DEFAULT_DESKTOP_INSTRUCTIONS = [
|
|
|
136
136
|
"You are controlling a native desktop through screenshots and mouse/keyboard actions.",
|
|
137
137
|
"Orient from the screenshot first: identify the active app, visible window, focused field, current page, and the specific target required by the task before acting.",
|
|
138
138
|
"Use deterministic entry points. For a website or web app, use the browser address bar only when a browser is clearly focused; otherwise open the browser through a visible Dock/app icon or OS search/launcher, then use the address bar. For app content, use visible in-app search, filters, or navigation controls.",
|
|
139
|
+
"Before using a terminal to run a shell command, confirm the terminal is idle and showing a prompt. If the visible terminal is occupied by a running foreground process, such as the Node.js script that started this session, do not type commands into it; use the OS launcher/Dock/search or a separate idle terminal session instead.",
|
|
139
140
|
"Do not open or use Command+Tab, Alt+Tab, Mission Control, or other cyclic app/window switchers unless the task explicitly asks to switch to the previous app. Cyclic switching is unreliable because the open-app order is unknown.",
|
|
140
141
|
"Do not click as a probe. Click only when the screenshot shows a specific visible target and the purpose of that click is clear from the task or current UI. Prefer named controls, fields, menu items, visible app icons, and address/search fields over unlabeled areas.",
|
|
141
142
|
"If the target is not visible, choose a deterministic recovery path: direct URL, OS search/launcher, in-app search, visible navigation, or a screenshot/wait when loading is visible. Do not repeat nearly identical clicks after no visible change.",
|