automify 0.1.11 → 0.3.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 +261 -36
- package/examples/browser-with-safety.js +7 -10
- package/examples/cli-qemu.js +28 -0
- package/examples/desktop-qemu.js +41 -0
- package/package.json +5 -2
- package/scripts/generate-argument-reference.js +3 -1
- package/scripts/qemu-image.js +151 -0
- package/src/index.d.ts +368 -10
- package/src/index.js +18 -38
- package/src/lib/adapter-toolkit.js +8 -4
- package/src/lib/anthropic-model-adapter.js +24 -13
- package/src/lib/argument-reference.js +60 -8
- package/src/lib/automify.js +97 -1
- package/src/lib/cli-automify.js +42 -3
- package/src/lib/computer-automify.js +45 -26
- package/src/lib/docker-cli-automify.js +2 -6
- package/src/lib/docker-desktop-computer.js +7 -13
- package/src/lib/file-data.js +6 -6
- package/src/lib/init.js +14 -3
- package/src/lib/local-desktop-computer.js +2 -1
- package/src/lib/openai-responses-client.js +10 -3
- package/src/lib/presets.js +50 -2
- package/src/lib/qemu-cli-automify.js +555 -0
- package/src/lib/qemu-desktop-computer.js +681 -0
- package/src/lib/qemu-runtime.js +654 -0
- package/src/lib/runtime.js +23 -2
- package/src/lib/screen-recording.js +184 -0
- package/src/lib/task.js +564 -0
- package/src/lib/virtual-shared-folder.js +3 -1
|
@@ -36,7 +36,7 @@ const DEFAULT_INSTRUCTIONS = [
|
|
|
36
36
|
"If the target is not visible, choose a deterministic recovery path: direct URL, terminal command, launcher/search, in-app search, visible navigation, or a screenshot/wait when loading is visible. Do not repeat nearly identical clicks after no visible change.",
|
|
37
37
|
"After any action that launches an app, navigates, submits input, changes windows, or might trigger loading, use the next screenshot to decide the next step. Stop when the requested result is known; do not keep interacting to confirm unnecessarily."
|
|
38
38
|
].join("\n");
|
|
39
|
-
const
|
|
39
|
+
const DOCKER_DESKTOP_OPTION_KEYS = new Set([
|
|
40
40
|
"preset",
|
|
41
41
|
"container",
|
|
42
42
|
"dockerCommand",
|
|
@@ -90,8 +90,8 @@ const VIRTUAL_DESKTOP_OPTION_KEYS = new Set([
|
|
|
90
90
|
"logFile",
|
|
91
91
|
"onUnknownAction"
|
|
92
92
|
]);
|
|
93
|
-
export const DOCKER_DESKTOP_COMPUTER_OPTION_KEYS =
|
|
94
|
-
const
|
|
93
|
+
export const DOCKER_DESKTOP_COMPUTER_OPTION_KEYS = DOCKER_DESKTOP_OPTION_KEYS;
|
|
94
|
+
const DOCKER_DESKTOP_CONTAINER_KEYS = new Set([
|
|
95
95
|
"docker",
|
|
96
96
|
"dockerCommand",
|
|
97
97
|
"image",
|
|
@@ -120,7 +120,7 @@ const VIRTUAL_DESKTOP_CONTAINER_KEYS = new Set([
|
|
|
120
120
|
"additionalAptPackages",
|
|
121
121
|
"installDependencies"
|
|
122
122
|
]);
|
|
123
|
-
const
|
|
123
|
+
const DOCKER_DESKTOP_DESKTOP_KEYS = new Set([
|
|
124
124
|
"startupCommand",
|
|
125
125
|
"windowManagerCommand",
|
|
126
126
|
"packages",
|
|
@@ -514,9 +514,6 @@ export class DockerDesktopSession {
|
|
|
514
514
|
}
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
-
export const createVirtualDesktopComputer = createDockerDesktopComputer;
|
|
518
|
-
export const DockerVirtualDesktopSession = DockerDesktopSession;
|
|
519
|
-
|
|
520
517
|
async function acquireDockerDesktopLock(options) {
|
|
521
518
|
if (!options.containerName) return null;
|
|
522
519
|
return acquireAdapterLock(`docker-desktop:${options.containerName}`, {
|
|
@@ -525,9 +522,9 @@ async function acquireDockerDesktopLock(options) {
|
|
|
525
522
|
}
|
|
526
523
|
|
|
527
524
|
function normalizeVirtualDesktopOptions(options = {}) {
|
|
528
|
-
assertKnownOptions("Docker desktop adapter", options,
|
|
529
|
-
assertKnownOptions("Docker desktop container", options.container,
|
|
530
|
-
assertKnownOptions("Docker desktop desktop", options.desktop,
|
|
525
|
+
assertKnownOptions("Docker desktop adapter", options, DOCKER_DESKTOP_OPTION_KEYS);
|
|
526
|
+
assertKnownOptions("Docker desktop container", options.container, DOCKER_DESKTOP_CONTAINER_KEYS);
|
|
527
|
+
assertKnownOptions("Docker desktop desktop", options.desktop, DOCKER_DESKTOP_DESKTOP_KEYS);
|
|
531
528
|
options = applyDockerDesktopPreset(options);
|
|
532
529
|
const container = options.container ?? {};
|
|
533
530
|
const viewport = options.viewport ?? {};
|
|
@@ -604,9 +601,6 @@ export function defaultDockerDesktopImage() {
|
|
|
604
601
|
return DEFAULT_IMAGE;
|
|
605
602
|
}
|
|
606
603
|
|
|
607
|
-
export const virtualDesktopDockerfile = dockerDesktopDockerfile;
|
|
608
|
-
export const defaultVirtualDesktopImage = defaultDockerDesktopImage;
|
|
609
|
-
|
|
610
604
|
function keys(values) {
|
|
611
605
|
const normalized = values.map((value) => key(value)).filter(Boolean);
|
|
612
606
|
if (normalized.length === 0) {
|
package/src/lib/file-data.js
CHANGED
|
@@ -76,7 +76,9 @@ export async function fileToEvaluate(file, options = {}) {
|
|
|
76
76
|
truncated ? `Content truncated to ${maxBytes} bytes from ${buffer.byteLength} bytes.` : null,
|
|
77
77
|
"",
|
|
78
78
|
text
|
|
79
|
-
]
|
|
79
|
+
]
|
|
80
|
+
.filter((part) => part != null)
|
|
81
|
+
.join("\n")
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -126,11 +128,9 @@ function mediaTypeForPath(path) {
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
function isTextMediaType(mediaType) {
|
|
129
|
-
return
|
|
130
|
-
"application/json",
|
|
131
|
-
|
|
132
|
-
"application/yaml"
|
|
133
|
-
].includes(mediaType);
|
|
131
|
+
return (
|
|
132
|
+
mediaType.startsWith("text/") || ["application/json", "application/xml", "application/yaml"].includes(mediaType)
|
|
133
|
+
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function positiveInteger(value) {
|
package/src/lib/init.js
CHANGED
|
@@ -2,10 +2,12 @@ import { createAutomify } from "./automify.js";
|
|
|
2
2
|
import { createBrowserAutomify, withBrowserAutomify } from "./browser-automify.js";
|
|
3
3
|
import { createCliAutomify } from "./cli-automify.js";
|
|
4
4
|
import { createDockerCliAutomify } from "./docker-cli-automify.js";
|
|
5
|
+
import { createVirtualCliAutomify } from "./qemu-cli-automify.js";
|
|
5
6
|
import {
|
|
6
7
|
createComputerAutomify,
|
|
7
8
|
createDockerComputerAutomify,
|
|
8
|
-
createLocalComputerAutomify
|
|
9
|
+
createLocalComputerAutomify,
|
|
10
|
+
createVirtualComputerAutomify
|
|
9
11
|
} from "./computer-automify.js";
|
|
10
12
|
import { createAnthropicModelAdapter } from "./anthropic-model-adapter.js";
|
|
11
13
|
import { AutomifyError } from "./errors.js";
|
|
@@ -57,7 +59,8 @@ export function initAutomify(options = {}) {
|
|
|
57
59
|
};
|
|
58
60
|
const computerDefaults = {
|
|
59
61
|
...defaults,
|
|
60
|
-
model: options.computerModel ?? provider.computerModel ?? provider.model
|
|
62
|
+
model: options.computerModel ?? provider.computerModel ?? provider.model,
|
|
63
|
+
screenRecording: options.screenRecording ?? options.recording ?? screenshots.recording
|
|
61
64
|
};
|
|
62
65
|
|
|
63
66
|
return {
|
|
@@ -114,8 +117,16 @@ export function initAutomify(options = {}) {
|
|
|
114
117
|
});
|
|
115
118
|
},
|
|
116
119
|
|
|
120
|
+
virtualComputer(computerOptions = {}) {
|
|
121
|
+
return createVirtualComputerAutomify({
|
|
122
|
+
...computerDefaults,
|
|
123
|
+
...computerOptions,
|
|
124
|
+
client
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
|
|
117
128
|
virtualCli(cliOptions = {}) {
|
|
118
|
-
return
|
|
129
|
+
return createVirtualCliAutomify({
|
|
119
130
|
...defaults,
|
|
120
131
|
...cliOptions,
|
|
121
132
|
client
|
|
@@ -29,6 +29,7 @@ const LOCAL_DESKTOP_OPTION_KEYS = new Set([
|
|
|
29
29
|
"actionDelayMs",
|
|
30
30
|
"instructions",
|
|
31
31
|
"screenshotPath",
|
|
32
|
+
"lockResource",
|
|
32
33
|
"pixelScale",
|
|
33
34
|
"mouseScaleX",
|
|
34
35
|
"mouseScaleY",
|
|
@@ -145,7 +146,7 @@ const DEFAULT_DESKTOP_INSTRUCTIONS = [
|
|
|
145
146
|
|
|
146
147
|
export async function createLocalDesktopComputer(options = {}) {
|
|
147
148
|
options = normalizeLocalDesktopOptions(options);
|
|
148
|
-
const releaseLock = await acquireAdapterLock("local-desktop", {
|
|
149
|
+
const releaseLock = await acquireAdapterLock(options.lockResource ?? "local-desktop", {
|
|
149
150
|
label: "local desktop adapter"
|
|
150
151
|
});
|
|
151
152
|
const setupStartedAt = Date.now();
|
|
@@ -41,7 +41,9 @@ export class OpenAIResponsesClient {
|
|
|
41
41
|
if (!response.ok) {
|
|
42
42
|
const message = data?.error?.message ?? data?.message ?? response.statusText;
|
|
43
43
|
const requestId = response.headers?.get?.("x-request-id") ?? response.headers?.get?.("openai-request-id");
|
|
44
|
-
const error = new AutomifyError(
|
|
44
|
+
const error = new AutomifyError(
|
|
45
|
+
`OpenAI Responses request failed${requestId ? ` (${requestId})` : ""}: ${message}`
|
|
46
|
+
);
|
|
45
47
|
error.status = response.status;
|
|
46
48
|
error.requestId = requestId;
|
|
47
49
|
if (attempt < this.maxRetries && isRetryableStatus(response.status)) {
|
|
@@ -144,13 +146,18 @@ function isRetryableStatus(status) {
|
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
function isRetryableError(error) {
|
|
147
|
-
return
|
|
149
|
+
return (
|
|
150
|
+
error?.name === "AbortError" ||
|
|
151
|
+
error?.code === "ECONNRESET" ||
|
|
152
|
+
error?.code === "ETIMEDOUT" ||
|
|
153
|
+
/fetch failed/i.test(error?.message ?? "")
|
|
154
|
+
);
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
function retryDelay(attempt, baseDelayMs, retryAfter) {
|
|
151
158
|
const retryAfterMs = Number(retryAfter) * 1000;
|
|
152
159
|
if (Number.isFinite(retryAfterMs) && retryAfterMs > 0) return retryAfterMs;
|
|
153
|
-
return Math.max(0, Number(baseDelayMs) || 0) *
|
|
160
|
+
return Math.max(0, Number(baseDelayMs) || 0) * 2 ** attempt;
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
function wait(ms) {
|
package/src/lib/presets.js
CHANGED
|
@@ -86,7 +86,38 @@ export function applyDockerCliPreset(options = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export
|
|
89
|
+
export function applyVirtualCliPreset(options = {}) {
|
|
90
|
+
switch (options.preset) {
|
|
91
|
+
case undefined:
|
|
92
|
+
case null:
|
|
93
|
+
return options;
|
|
94
|
+
case "repo":
|
|
95
|
+
return mergePreset(
|
|
96
|
+
{
|
|
97
|
+
command: REPO_COMMAND,
|
|
98
|
+
shared: {
|
|
99
|
+
hostPath: process.cwd(),
|
|
100
|
+
containerPath: "/workspace"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
options
|
|
104
|
+
);
|
|
105
|
+
case "locked-down-cli":
|
|
106
|
+
return mergePreset(
|
|
107
|
+
{
|
|
108
|
+
command: {
|
|
109
|
+
approval: "always",
|
|
110
|
+
allow: [],
|
|
111
|
+
block: [/^rm\b/, /^sudo\b/, /^curl\b/, /^wget\b/]
|
|
112
|
+
},
|
|
113
|
+
limits: { steps: 20 }
|
|
114
|
+
},
|
|
115
|
+
options
|
|
116
|
+
);
|
|
117
|
+
default:
|
|
118
|
+
throw unknownPreset("virtual CLI", options.preset, ["repo", "locked-down-cli"]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
90
121
|
|
|
91
122
|
export function applyDockerDesktopPreset(options = {}) {
|
|
92
123
|
switch (options.preset) {
|
|
@@ -107,7 +138,24 @@ export function applyDockerDesktopPreset(options = {}) {
|
|
|
107
138
|
}
|
|
108
139
|
}
|
|
109
140
|
|
|
110
|
-
export
|
|
141
|
+
export function applyVirtualDesktopPreset(options = {}) {
|
|
142
|
+
switch (options.preset) {
|
|
143
|
+
case undefined:
|
|
144
|
+
case null:
|
|
145
|
+
return options;
|
|
146
|
+
case "desktop-review":
|
|
147
|
+
return mergePreset(
|
|
148
|
+
{
|
|
149
|
+
viewport: { width: 1440, height: 900 },
|
|
150
|
+
waitMs: 750,
|
|
151
|
+
screenshotSettleMs: 500
|
|
152
|
+
},
|
|
153
|
+
options
|
|
154
|
+
);
|
|
155
|
+
default:
|
|
156
|
+
throw unknownPreset("virtual desktop", options.preset, ["desktop-review"]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
111
159
|
|
|
112
160
|
function mergePreset(defaults, options) {
|
|
113
161
|
return {
|