automify 0.1.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +401 -0
  4. package/SECURITY.md +17 -0
  5. package/examples/anthropic-provider.js +18 -0
  6. package/examples/browser-basic.js +30 -0
  7. package/examples/browser-with-safety.js +38 -0
  8. package/examples/claude-model-adapter.js +141 -0
  9. package/examples/cli-basic.js +20 -0
  10. package/examples/cli-docker.js +42 -0
  11. package/examples/custom-computer.js +18 -0
  12. package/examples/custom-model-adapter.js +48 -0
  13. package/examples/desktop-docker.js +37 -0
  14. package/examples/desktop-local.js +28 -0
  15. package/examples/evaluate-image.js +26 -0
  16. package/examples/files-and-shared-folder.js +42 -0
  17. package/package.json +74 -0
  18. package/scripts/generate-argument-reference.js +17 -0
  19. package/scripts/install-browser.js +12 -0
  20. package/scripts/install-desktop.js +281 -0
  21. package/src/index.d.ts +1049 -0
  22. package/src/index.js +83 -0
  23. package/src/lib/adapter-locks.js +93 -0
  24. package/src/lib/adapter-toolkit.js +239 -0
  25. package/src/lib/anthropic-model-adapter.js +451 -0
  26. package/src/lib/argument-reference.js +98 -0
  27. package/src/lib/automify.js +938 -0
  28. package/src/lib/browser-automify.js +89 -0
  29. package/src/lib/cli-automify.js +520 -0
  30. package/src/lib/computer-automify.js +103 -0
  31. package/src/lib/docker-cli-automify.js +517 -0
  32. package/src/lib/docker-desktop-computer.js +725 -0
  33. package/src/lib/errors.js +24 -0
  34. package/src/lib/file-data.js +140 -0
  35. package/src/lib/init.js +217 -0
  36. package/src/lib/local-desktop-computer.js +963 -0
  37. package/src/lib/model-adapter.js +32 -0
  38. package/src/lib/openai-responses-client.js +162 -0
  39. package/src/lib/output.js +57 -0
  40. package/src/lib/playwright-computer.js +363 -0
  41. package/src/lib/presets.js +141 -0
  42. package/src/lib/result.js +95 -0
  43. package/src/lib/runtime.js +471 -0
  44. package/src/lib/virtual-shared-folder.js +109 -0
  45. package/src/lib/zod-output.js +26 -0
  46. package/src/zod.d.ts +12 -0
  47. package/src/zod.js +5 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Changed `.do()` to accept one run object: use `{ data, output, ...options }` instead of a separate data argument.
6
+ - Added shared `initAutomify()` entrypoint.
7
+ - Added browser automation with Playwright.
8
+ - Added CLI automation with command approval and command policies.
9
+ - Added custom/native computer adapter surface.
10
+ - Added observability hooks and debug logging.
11
+ - Added unit, browser E2E, and optional live OpenAI E2E tests.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Automify contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,401 @@
1
+ # Automify
2
+
3
+ **AI computer use for browser, CLI, and desktop workflows in Node.js.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/automify.svg)](https://www.npmjs.com/package/automify)
6
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.18-brightgreen.svg)](https://nodejs.org/)
8
+
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
+
11
+ Computer use surfaces:
12
+
13
+ | Surface | Factory | What it does |
14
+ | -------------- | --------------------------- | ---------------------------------------------------------- |
15
+ | Browser | `automify.browser()` | Playwright browser automation with screenshots and actions |
16
+ | Desktop | `automify.localComputer()` | Native desktop computer use on the current machine |
17
+ | Docker desktop | `automify.dockerComputer()` | Containerized Linux desktop automation with screenshots |
18
+
19
+ Command use surfaces:
20
+
21
+ | Surface | Factory | What it does |
22
+ | ---------- | ---------------------- | ---------------------------------------------------- |
23
+ | CLI | `automify.cli()` | Terminal automation through model-requested commands |
24
+ | Docker CLI | `automify.dockerCli()` | Containerized terminal automation with shared files |
25
+
26
+ OpenAI and Anthropic models are supported, and any other model can be plugged in with a custom provider adapter.
27
+
28
+ ## What You Get
29
+
30
+ - Computer use for browser, local desktop, Docker desktop, and custom computer adapters.
31
+ - Command use for local CLI and Docker CLI runs.
32
+ - One `.do()` loop: give the model a task, let it request actions, return a structured result.
33
+ - Structured task input with `data` and structured output with `jsonOutput()`.
34
+ - Built-in OpenAI and Anthropic support, plus custom model adapters.
35
+ - Practical guardrails: domain allowlists, command policies, screenshot controls, max steps, and hooks.
36
+
37
+ Full docs live in [`docs/documentation.html`](docs/documentation.html). The shorter argument reference is [`docs/argument-reference.md`](docs/argument-reference.md).
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ npm install automify
43
+ ```
44
+
45
+ Chromium is installed by the package `postinstall` script. Skip it with:
46
+
47
+ ```bash
48
+ AUTOMIFY_SKIP_BROWSER_INSTALL=1 npm install automify
49
+ ```
50
+
51
+ Requirements: Node.js `18.18+` and a provider config. OpenAI examples use `gpt-5.5`.
52
+
53
+ Zod support is optional. Install Zod only if you want to build structured outputs from Zod schemas:
54
+
55
+ ```bash
56
+ npm install zod
57
+ ```
58
+
59
+ Automify does not require Zod for `jsonOutput()` or any browser, CLI, or desktop runtime.
60
+
61
+ ## Quick Start
62
+
63
+ ```js
64
+ import { initAutomify, jsonOutput } from "automify";
65
+
66
+ const automify = initAutomify({
67
+ provider: {
68
+ type: "openai",
69
+ apiKey: process.env.OPENAI_API_KEY,
70
+ model: "gpt-5.5"
71
+ }
72
+ });
73
+
74
+ const browser = await automify.browser({
75
+ // Optional: open a page before the task starts.
76
+ startUrl: "https://aldovincenti.github.io/automify/demo.html"
77
+ });
78
+
79
+ try {
80
+ const run = await browser.do("Add the person from data, then read the Latest saved record JSON block.", {
81
+ // Optional: structured task input.
82
+ data: {
83
+ firstName: "Ada",
84
+ lastName: "Lovelace"
85
+ },
86
+ // Optional: structured result shape.
87
+ output: jsonOutput("person_record", {
88
+ id: "string",
89
+ firstName: "string",
90
+ lastName: "string"
91
+ })
92
+ });
93
+
94
+ console.log(run.ok, run.parsed.id, run.parsed.firstName, run.parsed.lastName);
95
+ } finally {
96
+ await browser.close();
97
+ }
98
+ ```
99
+
100
+ ## Surfaces
101
+
102
+ ### Browser Computer Use
103
+
104
+ ```js
105
+ const browser = await automify.browser({
106
+ // Optional: open a page before the task starts.
107
+ startUrl: "https://example.com",
108
+ // Optional: choose the browser viewport.
109
+ viewport: { width: 1280, height: 800 },
110
+ // Optional: restrict browser navigation.
111
+ safety: { domains: ["example.com"] }
112
+ });
113
+
114
+ try {
115
+ const run = await browser.do("Extract the support email.", {
116
+ // Optional: structured result shape.
117
+ output: jsonOutput("support_contact", { email: "string" })
118
+ });
119
+
120
+ console.log(run.parsed.email);
121
+ } finally {
122
+ await browser.close();
123
+ }
124
+ ```
125
+
126
+ Use browser computer use for dashboards, admin panels, forms, and browser-only workflows.
127
+
128
+ ### CLI Command Use
129
+
130
+ ```js
131
+ const cli = automify.cli({
132
+ // Optional: constrain command execution.
133
+ command: {
134
+ cwd: process.cwd(),
135
+ allow: ["npm test", "npm run build", "ls", "pwd"]
136
+ }
137
+ });
138
+
139
+ await cli.do("Run the tests and summarize failures");
140
+ ```
141
+
142
+ Use Docker CLI when command execution should happen inside an isolated container:
143
+
144
+ ```js
145
+ import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
146
+ import { join } from "node:path";
147
+ import { tmpdir } from "node:os";
148
+ import { initAutomify } from "automify";
149
+
150
+ const sharedDir = await mkdtemp(join(tmpdir(), "automify-docker-cli-"));
151
+ const dataDir = join(sharedDir, "data");
152
+ const reportPath = join(dataDir, "report.csv");
153
+ const summaryPath = join(dataDir, "summary.json");
154
+
155
+ await mkdir(dataDir, { recursive: true });
156
+ await writeFile(
157
+ reportPath,
158
+ "region,customer,revenue\n" + "North,Ada Corp,1250\n" + "South,Byron Ltd,980\n" + "North,Lovelace Labs,2230\n"
159
+ );
160
+ await writeFile(summaryPath, "{}\n");
161
+
162
+ const automify = initAutomify({
163
+ provider: {
164
+ type: "openai",
165
+ apiKey: process.env.OPENAI_API_KEY,
166
+ model: "gpt-5.5"
167
+ }
168
+ });
169
+
170
+ const cli = automify.dockerCli({
171
+ // Optional: choose resource limits without changing the default image.
172
+ container: { cpus: 1, memory: "1g" },
173
+ // Optional: install Debian packages before commands run.
174
+ additionalAptPackages: ["coreutils", "nodejs"],
175
+ // Optional: mount a host folder into the container workspace.
176
+ shared: { hostPath: sharedDir, containerPath: "/workspace" }
177
+ });
178
+
179
+ try {
180
+ await cli.do(
181
+ "Read data/report.csv, use a Node.js script to calculate revenue by region, update data/summary.json with the result, and report the top region"
182
+ );
183
+ const summary = JSON.parse(await readFile(summaryPath, "utf8"));
184
+ console.log(summary);
185
+ console.log("Shared output file:", summaryPath);
186
+ } finally {
187
+ await cli.close();
188
+ }
189
+ ```
190
+
191
+ ### Desktop Computer Use
192
+
193
+ Local desktop computer use is optional because OS control needs permissions:
194
+
195
+ ```bash
196
+ npm run install:desktop
197
+ ```
198
+
199
+ ```js
200
+ import { initAutomify } from "automify";
201
+
202
+ const automify = initAutomify({
203
+ provider: {
204
+ type: "openai",
205
+ apiKey: process.env.OPENAI_API_KEY,
206
+ model: "gpt-5.5"
207
+ }
208
+ });
209
+
210
+ const desktop = await automify.localComputer();
211
+
212
+ try {
213
+ await desktop.do(
214
+ "Open the Calendar app installed on this computer, find the next event after today, and summarize it. Do not create or edit events."
215
+ );
216
+ } finally {
217
+ await desktop.close();
218
+ }
219
+ ```
220
+
221
+ For isolated Linux desktop computer use, use Docker:
222
+
223
+ ```js
224
+ import { initAutomify } from "automify";
225
+
226
+ const automify = initAutomify({
227
+ provider: {
228
+ type: "openai",
229
+ apiKey: process.env.OPENAI_API_KEY,
230
+ model: "gpt-5.5"
231
+ }
232
+ });
233
+
234
+ const desktop = await automify.dockerComputer({
235
+ // Optional: choose resource limits or another image.
236
+ container: { cpus: 2, memory: "2g" },
237
+ // Required: launch an app when the desktop starts.
238
+ desktop: { startupCommand: "xterm" }
239
+ });
240
+
241
+ try {
242
+ await desktop.do("Use the open terminal to run 'uname -a' and summarize the system information shown on screen");
243
+ } finally {
244
+ await desktop.close();
245
+ }
246
+ ```
247
+
248
+ Local desktop computer use takes an exclusive cross-process lock until `close()`. Docker desktop locks are scoped to the container name, so different containers can run in parallel.
249
+
250
+ ### Custom Computer Use
251
+
252
+ ```js
253
+ const computer = {
254
+ execute: async (action, context) => remoteDesktop.execute(action, context),
255
+ screenshot: async (context) => remoteDesktop.screenshot(context)
256
+ };
257
+
258
+ await automify.computer({ computer }).do("Use the remote app with the supplied ticket.", {
259
+ // Optional: structured task input.
260
+ data: { ticketId: "SUP-123", priority: "high" }
261
+ });
262
+ ```
263
+
264
+ Custom computer adapters can expose `environment`, `displayWidth`, and `displayHeight` when they control a fixed remote target. Built-in local and Docker desktop adapters infer or choose those values for you.
265
+
266
+ ## Input And Output
267
+
268
+ Computer use and command use surfaces share the same `.do()` option shape:
269
+
270
+ ```js
271
+ const run = await browser.do("Create the lead from data and return the saved record.", {
272
+ // Optional: structured task input.
273
+ data: { firstName: "Ada", lastName: "Lovelace" },
274
+ // Optional: files the model should inspect directly.
275
+ evaluate: [{ path: "/tmp/reference.png", detail: "high" }],
276
+ // Optional: structured result shape.
277
+ output: jsonOutput("lead", {
278
+ id: "string",
279
+ firstName: "string",
280
+ lastName: "string"
281
+ }),
282
+ // Optional: per-run limits.
283
+ limits: { steps: 20 },
284
+ // Optional: save run screenshots.
285
+ screenshots: { final: "/tmp/automify-final.png" }
286
+ });
287
+ ```
288
+
289
+ - `data` is structured JSON for the task.
290
+ - `evaluate` sends images or text files directly to the model.
291
+ - `shared` and `sharedFiles` expose files inside Docker CLI or Docker desktop runs.
292
+ - `jsonOutput()` requests structured JSON and makes parsed output available as `run.parsed`.
293
+
294
+ ### Optional Zod Output
295
+
296
+ If your app already uses Zod 4, you can use the optional Zod adapter instead of writing compact shapes or JSON Schema by hand. Install `zod` in your app and import from the dedicated `automify/zod` subpath:
297
+
298
+ ```js
299
+ import { z } from "zod";
300
+ import { zodOutput } from "automify/zod";
301
+
302
+ const Lead = z.object({
303
+ id: z.string(),
304
+ firstName: z.string(),
305
+ lastName: z.string()
306
+ });
307
+
308
+ const run = await browser.do("Create the lead and return it.", {
309
+ output: zodOutput("lead", Lead)
310
+ });
311
+
312
+ console.log(run.parsed.id);
313
+ ```
314
+
315
+ `zodOutput()` is not part of the main `automify` import on purpose. Zod is an optional peer dependency, so projects that only use `jsonOutput()` do not need to install it.
316
+
317
+ At runtime, `zodOutput()` does two things:
318
+
319
+ - It converts the Zod schema to JSON Schema with Zod 4's `z.toJSONSchema()` and sends that schema to the model.
320
+ - It validates the parsed model response with the original schema's `schema.parse()` before assigning `run.parsed`.
321
+
322
+ Pass `{ parse: false }` if you want Automify to request the Zod-derived JSON Schema but skip automatic parsing and Zod validation of the final response.
323
+
324
+ ## Safety
325
+
326
+ Before running computer use against real accounts or user data:
327
+
328
+ | Area | Recommendation |
329
+ | ------- | --------------------------------------------------------------------------------------------------------- |
330
+ | Scope | Use dedicated accounts, narrow browser allowlists, command policies, and isolated desktops or containers. |
331
+ | Data | Pass task input through `data`; request application output with `jsonOutput()` instead of parsing prose. |
332
+ | Safety | Add human approval for sensitive CLI commands, browser actions, or externally visible operations. |
333
+ | Privacy | Redact screenshots before model upload when screens can contain secrets or regulated data. |
334
+ | Audit | Use `hooks`, `screenshots.actions`, `logFile`, and `trace: true` for workflows that need review. |
335
+
336
+ ## Providers
337
+
338
+ ```js
339
+ const automify = initAutomify({
340
+ provider: {
341
+ type: "openai",
342
+ apiKey: process.env.OPENAI_API_KEY,
343
+ model: "gpt-5.5"
344
+ }
345
+ });
346
+ ```
347
+
348
+ Anthropic and custom model gateways are supported too:
349
+
350
+ ```js
351
+ const automify = initAutomify({
352
+ provider: {
353
+ type: "anthropic",
354
+ apiKey: process.env.ANTHROPIC_API_KEY,
355
+ model: "claude-sonnet-4-20250514",
356
+ // Optional: provider-specific settings.
357
+ maxTokens: 4096,
358
+ betas: ["computer-use-2025-01-24"]
359
+ }
360
+ });
361
+ ```
362
+
363
+ ```js
364
+ const automify = initAutomify({
365
+ provider: {
366
+ type: "custom",
367
+ model: "my-model",
368
+ // Optional: adapt a custom model gateway.
369
+ adapter: {
370
+ async respond(payload, context) {
371
+ return { id: "custom_response", output: [] };
372
+ }
373
+ }
374
+ }
375
+ });
376
+ ```
377
+
378
+ Use the adapter toolkit when a custom provider needs to emit computer use actions. See `examples/custom-model-adapter.js` and `examples/claude-model-adapter.js`.
379
+
380
+ ## Examples
381
+
382
+ - `examples/browser-basic.js`
383
+ - `examples/browser-with-safety.js`
384
+ - `examples/cli-basic.js`
385
+ - `examples/cli-docker.js`
386
+ - `examples/desktop-local.js`
387
+ - `examples/desktop-docker.js`
388
+ - `examples/custom-computer.js`
389
+ - `examples/custom-model-adapter.js`
390
+
391
+ ## Tests
392
+
393
+ ```bash
394
+ npm test
395
+ npm run test:e2e
396
+ OPENAI_API_KEY=... npm run test:live
397
+ ```
398
+
399
+ ## License
400
+
401
+ MIT
package/SECURITY.md ADDED
@@ -0,0 +1,17 @@
1
+ # Security Policy
2
+
3
+ `automify` can control browsers, execute shell commands, and integrate with native computer adapters. Treat every automation session as privileged code execution.
4
+
5
+ ## Recommendations
6
+
7
+ - Use dedicated test accounts and isolated browser contexts.
8
+ - Configure `allowedDomains` for browser automation.
9
+ - Keep CLI `approval: "always"` unless your command policy is narrow and well tested.
10
+ - Use `allowedCommands` and `blockedCommands` for CLI automation.
11
+ - Keep `maxSteps` bounded.
12
+ - Redact screenshots before they are sent to OpenAI when pages may contain sensitive information.
13
+ - Do not automate payments, destructive administrative actions, or private user data without explicit human approval.
14
+
15
+ ## Reporting Issues
16
+
17
+ Open a private security advisory in the repository if available, or contact the maintainers through the repository issue tracker with minimal reproduction details.
@@ -0,0 +1,18 @@
1
+ import { initAutomify } from "../src/index.js";
2
+
3
+ const automify = initAutomify({
4
+ provider: {
5
+ type: "anthropic",
6
+ apiKey: process.env.ANTHROPIC_API_KEY,
7
+ model: "claude-sonnet-4-20250514",
8
+ maxTokens: 4096,
9
+ betas: ["computer-use-2025-01-24"]
10
+ }
11
+ });
12
+
13
+ const cli = automify.cli({
14
+ cwd: process.cwd()
15
+ });
16
+
17
+ const result = await cli.do("Inspect this project and tell me how to run the tests");
18
+ console.log(result.response);
@@ -0,0 +1,30 @@
1
+ import { initAutomify, jsonOutput } from "../src/index.js";
2
+
3
+ const automify = initAutomify({
4
+ provider: {
5
+ type: "openai",
6
+ apiKey: process.env.OPENAI_API_KEY,
7
+ model: "gpt-5.5"
8
+ }
9
+ });
10
+
11
+ const browser = await automify.browser({
12
+ startUrl: "https://aldovincenti.github.io/automify/demo.html"
13
+ });
14
+
15
+ try {
16
+ const result = await browser.do("Add the person from data, then read the Latest saved record JSON block.", {
17
+ data: {
18
+ firstName: "Grace",
19
+ lastName: "Hopper"
20
+ },
21
+ output: jsonOutput("person_record", {
22
+ id: "string",
23
+ firstName: "string",
24
+ lastName: "string"
25
+ })
26
+ });
27
+ console.log(result.ok, result.parsed);
28
+ } finally {
29
+ await browser.close();
30
+ }
@@ -0,0 +1,38 @@
1
+ import { initAutomify } from "../src/index.js";
2
+
3
+ const automify = initAutomify({
4
+ provider: {
5
+ type: "openai",
6
+ apiKey: process.env.OPENAI_API_KEY,
7
+ model: "gpt-5.5"
8
+ },
9
+ debug: true
10
+ });
11
+
12
+ await automify.withBrowser(
13
+ {
14
+ startUrl: "https://example.com",
15
+ safety: {
16
+ domains: ["example.com"]
17
+ },
18
+ hooks: {
19
+ step: ({ phase, action }) => {
20
+ console.log("Step:", phase, action);
21
+ }
22
+ }
23
+ },
24
+ async (browser) => {
25
+ return browser.do(
26
+ "Find the contact page and report the support address",
27
+ {
28
+ safety: {
29
+ onCheck: async ({ checks, action }) => {
30
+ console.log("Safety checks:", checks);
31
+ console.log("Action:", action);
32
+ return true;
33
+ }
34
+ }
35
+ }
36
+ );
37
+ }
38
+ );
@@ -0,0 +1,141 @@
1
+ import {
2
+ computerCall,
3
+ getComputerTool,
4
+ getInputText,
5
+ getLastComputerScreenshot,
6
+ initAutomify,
7
+ message,
8
+ response,
9
+ runCommandCall
10
+ } from "../src/index.js";
11
+
12
+ function createClaudeModelAdapter({ anthropicApiKey, fetchImpl = fetch } = {}) {
13
+ return {
14
+ async respond(payload, context) {
15
+ const claudeRequest = toClaudeRequest(payload, context);
16
+
17
+ // Uncomment this block when wiring a real Anthropic account.
18
+ //
19
+ // const res = await fetchImpl("https://api.anthropic.com/v1/messages", {
20
+ // method: "POST",
21
+ // headers: {
22
+ // "x-api-key": anthropicApiKey,
23
+ // "anthropic-version": "2023-06-01",
24
+ // "content-type": "application/json"
25
+ // },
26
+ // body: JSON.stringify(claudeRequest)
27
+ // });
28
+ // const claude = await res.json();
29
+ // return fromClaudeResponse(claude, context);
30
+
31
+ console.log("Claude request shape:", claudeRequest);
32
+ return response({ output: [message("Claude adapter is wired. Connect fetch to call Anthropic.")] });
33
+ }
34
+ };
35
+ }
36
+
37
+ function toClaudeRequest(payload, context) {
38
+ const screenshot = getLastComputerScreenshot(payload);
39
+
40
+ return {
41
+ model: payload.model,
42
+ max_tokens: payload.max_tokens ?? 4096,
43
+ messages: [
44
+ {
45
+ role: "user",
46
+ content: [
47
+ { type: "text", text: getInputText(payload) || "Continue." },
48
+ ...(screenshot
49
+ ? [
50
+ {
51
+ type: "image",
52
+ source: {
53
+ type: "base64",
54
+ media_type: screenshot.mediaType,
55
+ data: screenshot.base64
56
+ }
57
+ }
58
+ ]
59
+ : [])
60
+ ]
61
+ }
62
+ ],
63
+ tools: toClaudeTools(payload, context)
64
+ };
65
+ }
66
+
67
+ function toClaudeTools(payload, context) {
68
+ if (context.surface === "cli") {
69
+ return [
70
+ {
71
+ name: "run_command",
72
+ description: "Run a shell command.",
73
+ input_schema: {
74
+ type: "object",
75
+ properties: {
76
+ command: { type: "string" }
77
+ },
78
+ required: ["command"]
79
+ }
80
+ }
81
+ ];
82
+ }
83
+
84
+ if (getComputerTool(payload)) {
85
+ return [
86
+ {
87
+ name: "computer",
88
+ description: "Control the visible computer with mouse and keyboard actions.",
89
+ input_schema: {
90
+ type: "object",
91
+ properties: {
92
+ action: { type: "object" }
93
+ },
94
+ required: ["action"]
95
+ }
96
+ }
97
+ ];
98
+ }
99
+
100
+ return [];
101
+ }
102
+
103
+ function fromClaudeResponse(claude, context) {
104
+ const toolUse = claude.content?.find((item) => item.type === "tool_use");
105
+ const text = claude.content?.find((item) => item.type === "text")?.text;
106
+
107
+ if (!toolUse) {
108
+ return response({ id: claude.id, output: [message(text ?? "")] });
109
+ }
110
+
111
+ if (toolUse.name === "run_command") {
112
+ return response({
113
+ id: claude.id,
114
+ output: [runCommandCall(toolUse.input.command, { callId: toolUse.id })]
115
+ });
116
+ }
117
+
118
+ if (toolUse.name === "computer") {
119
+ return response({
120
+ id: claude.id,
121
+ output: [computerCall(toolUse.input.action, { callId: toolUse.id })]
122
+ });
123
+ }
124
+
125
+ return response({ id: claude.id, output: [message(`Unsupported tool: ${toolUse.name}`)] });
126
+ }
127
+
128
+ const automify = initAutomify({
129
+ provider: {
130
+ type: "custom",
131
+ model: "claude-computer-use-model",
132
+ options: {
133
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY
134
+ },
135
+ adapter: (options) => createClaudeModelAdapter(options)
136
+ }
137
+ });
138
+
139
+ const cli = automify.cli({ cwd: process.cwd() });
140
+ const result = await cli.do("Explain how this Claude adapter maps tool calls.");
141
+ console.log(result.response);
@@ -0,0 +1,20 @@
1
+ import { initAutomify } from "../src/index.js";
2
+
3
+ const automify = initAutomify({
4
+ provider: {
5
+ type: "openai",
6
+ apiKey: process.env.OPENAI_API_KEY,
7
+ model: "gpt-5.5"
8
+ }
9
+ });
10
+
11
+ const cli = automify.cli({
12
+ command: {
13
+ cwd: process.cwd(),
14
+ allow: ["npm", "node", "ls", "pwd"],
15
+ block: [/^rm\b/, /^git push\b/]
16
+ }
17
+ });
18
+
19
+ const result = await cli.do("Inspect this project and tell me how to run its tests");
20
+ console.log(result.response);