For the complete documentation index, see llms.txt. This page is also available as Markdown.

Imports and Package Management

Agents with versions older than 1.1.76 have limitations (pure ESM packages and native packages are generally not supported). Install version 1.1.76 for broader package support.

Use standard JavaScript imports like import dayjs from "dayjs@1.11.11" or use the zw API zw.import() for custom options to install (if needed) and import packages during a TaskBot run. ZeroWork installs missing packages and makes them available to your code.

Manage packages with zw.packages.list(), zw.packages.uninstall(), and zw.packages.uninstallAll().

API zw.import(), zw.packages.*

// @zw-run-locally

// Use desktop automation in ZeroWork

import { execSync } from "child_process";
import { keyboard } from "@computer-use/nut-js";

// Open an editor depending on OS
if (process.platform === "win32") {
  execSync("start notepad");
} else if (process.platform === "darwin") {
  execSync(`osascript -e 'tell application "TextEdit" to activate' -e 'tell application "TextEdit" to make new document'`);
} else {
  execSync("gedit || xed || kate || nano", { shell: "/bin/bash" });
}

// Wait briefly for the editor window to appear
await zw.delay({ min: 2_000 });

// Type into the editor
await keyboard.type("Hello from ZeroWork desktop automation!");

1. Imports

Standard Imports

You can use standard ESM or CommonJS syntax. Any such import installs the package if needed, then loads it.

Notes

  • Installed packages are available to any TaskBot by default. To scope per TaskBot, use zw.import() with { isolate: true } (see details further below).

  • If a package isnโ€™t used for one week, it's removed automatically. To change this time window or prevent automatic uninstall, use zw.import() with uninstallIfUnusedFor (see details further below).

  • Subpaths like "lodash/chunk" and Git repositories are supported.

  • You can disable auto-imports by adding a comment // @zw-disable-auto-import


Imports with zw.import()

Use zw.import() when you need options or want to import several packages at once.

At a Glance

  • async await zw.import(pkg: string | string[] | { [key: string]: string }, options?: ImportOptions) โ†’ installs if needed and returns the loaded module(s)

Examples

Single package with options

Multiple packages as an array

Named mapping as an object

Installing from Git (HTTPS only)

Never uninstall

Import Options

  • uninstallIfUnusedFor (default: 168) Defaults to 168 hours (one week). Set to null to keep the package on the device indefinitely.

  • isolate (default: false) When true, the package is scoped to this TaskBot only and isnโ€™t visible in other TaskBots. Useful when different TaskBots need different versions of the same package. Note: If the TaskBot is deleted, the package isnโ€™t removed automatically; you can manage it later with zw.packages.* (see details further below).

  • preferDefault (default: true) โ€” advanced Returns module.default when present (otherwise the module object). This only changes the return shape; it doesnโ€™t affect how the module is resolved.

Supported Package Inputs

You can pass any of the following to zw.import() as the first argument (package input).

  • String "lodash", "lodash@4.17.21", "lodash/chunk", "https://github.com/user/repo.git#main" or "git+https://github.com/user/repo.git#main"

  • Array of strings ["dayjs@1.11.11", "lodash@4.17.21"]

  • Object mapping { util: "lodash@4.17.21", time: "dayjs@1.11.11" }

Invalid inputs that are rejected

  • Tarball URLs, local file paths, and directories

  • Non-HTTPS Git URLs


Built-in Packages

The following packages and Node core modules are pre-bundled and resolve without installation.

  • Node core modules such as fs, os, etc.

  • axios pinned to ^1.6.6

  • playwright pinned to ^1.45.0

They donโ€™t appear in zw.packages.list(), and you canโ€™t change their versions or uninstall them.

Note on Playwright

The pre-bundled playwright import gives you the Playwright API, but the Agent does not bundle the browser binaries Playwright normally downloads. TaskBots run on the Chrome (or the executablePath of another Chromium-based browser) installed on your device. So playwright.chromium.launch() with no options fails; launch with the Chrome channel instead: await playwright.chromium.launch({ channel: "chrome" }). That said, in most cases you don't need this because you can use zw.browserContext.launch().


Reusing References

Import once and reuse the reference, as is generally best practice. The package wonโ€™t reinstall if itโ€™s already installed, but package parsing and resolution still add overhead.


2. Package Management

At a Glance

  • async await zw.packages.list() โ†’ returns string[] of package IDs.

  • async await zw.packages.uninstall(id: string) โ†’ removes one.

  • async await zw.packages.uninstallAll() โ†’ removes all user-managed packages.

Built-ins (axios@^1.6.6, playwright@^1.45.0, and Node core modules like os, fs, etc.) do not appear in list() and cannot be uninstalled.

Whatโ€™s a Package ID?

A package ID uniquely identifies an installed package instance. Itโ€™s an opaque string made of an optional isolation prefix (<taskbotId>_), a lower-cased, sanitized package name, @, and a version tag (<semver>, latest, git, or a commit hash). It may differ from your original import string (subpaths arenโ€™t included; characters are normalized).

Examples

Use these IDs with zw.packages.uninstall(id).


Examples

List packages

Uninstall by matching name/version in the package ID

Uninstall all for a specific TaskBot

Uninstall all


3. Local and Browser Execution

Package installation and management run locally only. Standard import/require, zw.import(), and zw.packages.* are supported only when the Write JS block runs outside the browser context. Enable the Run locally checkbox in the block UI, or add the comment // @zw-run-locally.

โœ… You can still use imported packages in the browser by exposing functions from a local block (see below).

How to Use Imported Packages in the Browser

Here's how:

  • Import in a block that runs locally (e.g. use @zw-run-locally) and expose the needed function.

  • Call the function from a later block that runs in the browser.


4. Troubleshooting

I need a package inside an in-browser execution but I can't import it there.

See 3. Local and Browser Execution. That section explains how you should import it in a locally running Write JS block first and then use the imported package's function(s) in a Write JS block running in the browser.

Some packages require extra pre-installed tools on your machine.

In such cases, the error typically mentions "node-gyp" or "gyp ERR!" like in this example:

The package "robotjs" was not installed. Error details: command failed. (pkg: robotjs@0.6.0, event: install, code: 1, script: node-gyp rebuild, stderr: ...gyp ERR! ...)

This usually means that the package tried to compile native code and the required build tools may be missing, such as Xcode Command Line Tools (Mac), Visual Studio Build Tools with C++ (Windows), or build-essential (Linux).

Note that the "node-gyp" / "gyp ERR!" error can also happen for other reasons: the package may not support the Agent's Node version (see below: Node version mismatch...), may not support your device's CPU architecture, may depend on a missing system library, or may contain a bug itself. The stderr: part of the error contains the compiler output that usually identifies the actual problem, so check it first before installing anything.

Node version mismatch: Package fails with a syntax error or 'X is not a function'.

The package may require a newer Node.js version than the Agent ships with (currently Node 22). Packages state their supported Node.js versions in the engines field of their package.json, usually also in their release notes. The failure can take different forms: a syntax error, a missing API at runtime ("X is not a function"), or a load error. The fix is to pin the newest version of the package whose engines range includes Node 22. If no such version exists, use an alternative package.

I'd like to import from a local file.

Local file paths, directories, and tarballs are not supported in zw.import() or standard import/require statements.

Instead:

  • For your own code, push it to a Git repository and import the HTTPS Git URL. Bonus: This works on every device the TaskBot runs on.

  • Load the file directly with Node:

The import takes forever and never resolves.

Large packages legitimately take a while. Anything that downloads a browser (puppeteer, @playwright/browser-*) moves hundreds of MB on first install and can take several minutes to resolve.

๐Ÿ’กWhen installing browser automation libraries like puppeteer, prefer installing them without browser binaries. For example, you can install puppeteer-core, which can turn a 500 MB installation into just a few MB. Then you can launch with your already installed browser: await puppeteer.launch({ channel: "chrome" }).

Separately, some package versions can ship a faulty install script. For example, some older puppeteer releases (v21 and earlier) stall at the very end of installation even though the download itself succeeds. (Note: current puppeteer versions install cleanly.)

If an install shows no progress for 15+ minutes, stop the run and retry with a different package version. If that doesn't help and you can't find any publicly reported installer bugs or issues for the package, file a bug report and we'll investigate: Report a bug.

Error: "...This git repository requires a build step, which needs the npm command..."

This typically happens with imports from Git URLs. The repository declares a build step (a build, prepare, or install script in its package.json) that the Agent cannot run.

What to do:

  • If the package is published to the npm registry, import it by name instead of using a Git URL. Published registry packages usually include the built output and don't require the repository build step.

  • If you're on Windows or Linux, install Node.js (which includes npm) and restart the Agent. Note that this won't help on macOS, where desktop apps don't see the PATH locations Node.js installs into.

  • For your own private repositories, commit runnable code and remove build-related scripts from package.json (i.e. commit the built output if there is a build).

Error: "The package ... was not installed. ... command failed (pkg: ..., stderr: ...)".

The pkg: field names the dependency whose install script actually failed. This is often a transitive dependency, not the package you imported. The stderr: output contains the underlying reason. You can investigate the issue using that error with AI and/or file a bug report for us to investigate: Report a bug.

Browser-only packages: "...appears to be built for browsers".

Some packages are built to run inside a web page, not in Node. Their code needs a browser environment. Importing them locally fails with an error like:

... This package appears to be built for browsers rather than Node.

โœ… These packages can still be used.

Here's how:

  • Find the package's bundled build. (Many ship one in their dist/ folder; look for a plain script, not raw ESM source.)

  • Save it locally.

  • Add it under Scripts in Browser Launch Settings (or in Launch Browser block, or via await zw.browserContext.launch({ launchConfig: { scripts: [{ path: "/Users/me/scripts/guard.js" }] } }) as documented in launchConfig.scripts).

Example: SingleFile

Here's an end-to-end example with SingleFile (captures a complete web page as one HTML string):

  1. Step 1: Save the bundle to your machine. You can use the terminal, download it manually, or use a Write JS block. What matters is saving a runnable script: SingleFile publishes its bundle wrapped in a JavaScript string (made for programmatic injection), so it needs one unwrap step. The example below downloads, unwraps, and saves it for you.

๐Ÿ’ก This unwrap step is specific to SingleFile. Most browser-oriented packages ship a ready-to-use bundle you can download as-is. Look for a dist/ folder in the repository or fetch it from a CDN like jsdelivr (https://cdn.jsdelivr.net/npm/<package>/dist/...).

  1. Step 2: In your TaskBot's Browser Launch Settings, open the Scripts section, add a script with type Path and the file path from Step 1.

  2. Step 3: Use it from a browser-context Write JS block. The script runs on every page the TaskBot opens, so its singlefile global is available (launch or relaunch the browser after changing Scripts):

Last updated