How the Page-Agent Monorepo Structure Organizes Inter-Package Dependencies
The Alibaba Page-Agent repository leverages npm workspaces to maintain six internal packages in strict topological order, enabling downstream modules like @page-agent/core to import upstream modules like @page-agent/page-controller via standard npm specifiers that resolve to local source code.
The alibaba/page-agent project implements a browser automation agent using a modular monorepo structure that separates concerns across distinct npm packages. This architecture isolates the DOM controller, LLM client, UI components, and core agent logic into discrete workspaces while preserving a unified build pipeline.
Workspace Architecture and Package Topology
The repository defines six active workspaces in the root package.json array. The order follows the dependency graph—packages that serve as dependencies for others appear earlier in the list to ensure correct linking during installation.
| Workspace | Purpose | Internal Dependencies |
|---|---|---|
packages/page-controller |
DOM extraction, element actions, and visual masks | None |
packages/ui |
Reusable UI components and internationalization | None |
packages/llms |
LLM client with reflection-before-action model | None |
packages/core |
Core agent logic (headless, no UI) | @page-agent/llms, @page-agent/page-controller |
packages/page-agent |
Public entry point adding UI panel on top of core | @page-agent/core, @page-agent/llms, @page-agent/page-controller, @page-agent/ui |
packages/extension |
Browser extension scaffolding (WXT + React) | None |
packages/website |
Documentation site | None |
The root package.json declares these workspaces at lines 6–13 in the exact topological sequence required for successful builds. This ordering guarantees that npm install can hoist shared third-party dependencies while correctly linking intra-repo packages.
Declaring Dependencies Between Packages
Each package lists its runtime dependencies using the published package names (@page-agent/*), allowing npm to resolve them to the locally built versions within the monorepo.
In packages/core/package.json (lines 45–48), the core module declares its upstream requirements:
{
"dependencies": {
"@page-agent/llms": "workspace:*",
"@page-agent/page-controller": "workspace:*"
}
}
Similarly, the public entry point in packages/page-agent/package.json (lines 46–51) aggregates all internal components:
{
"dependencies": {
"@page-agent/core": "workspace:*",
"@page-agent/llms": "workspace:*",
"@page-agent/page-controller": "workspace:*",
"@page-agent/ui": "workspace:*"
}
}
Because these references use the workspace protocol, running npm install at the repository root automatically symlinks the local packages together. This eliminates the need to publish interim versions to npm during development.
Build Pipeline and Topological Execution
Running npm run build executes the build:libs script, which triggers each workspace’s individual build process (most use Vite). The build system respects the dependency graph, ensuring that @page-agent/core compiles before @page-agent/page-agent attempts to bundle it.
Key characteristics of this pipeline include:
- Isolated Artifacts – Each package maintains its own
distdirectory, source maps, and TypeScript declarations, preventing build pollution between UI components and headless logic. - Explicit Local Links – Using the
@page-agent/namespace ensures that import statements remain identical whether the code runs inside the monorepo or is consumed from the npm registry. - Workspace Hoisting – The topological order in the root
workspacesarray allows npm to deduplicate shared third-party dependencies (like React or Vite) across packages while preserving strict intra-repo linking.
Cross-Package Import Patterns
The dependency organization enables straightforward TypeScript imports between workspaces. These patterns demonstrate how the monorepo structure facilitates code reuse while maintaining type safety.
The core package imports DOM and LLM utilities from its upstream workspaces in packages/core/src/tools/index.ts:
import { PageController } from '@page-agent/page-controller'
import { LLMClient } from '@page-agent/llms'
export class ClickTool {
constructor(private pc: PageController, private llm: LLMClient) {}
async click(index: number) {
await this.pc.clickElement(index)
}
}
The page-agent public package composes core logic with UI components in packages/page-agent/src/PageAgent.ts:
import { PageAgentCore } from '@page-agent/core'
import { Panel } from '@page-agent/ui'
export class PageAgent extends PageAgentCore {
private panel = new Panel()
// ...
}
Even the documentation site consumes the built packages, as seen in packages/website/src/App.tsx:
import { PageAgent } from '@page-agent/page-agent'
These imports resolve correctly because npm maps the @page-agent/* specifiers to the local directories defined in the workspace configuration.
Summary
- The Page-Agent monorepo uses npm workspaces to manage six internal packages in topological order, ensuring dependencies build before dependents.
- Internal dependencies are declared via standard npm specifiers (
@page-agent/core) with the workspace protocol, enabling seamless local development and future publishing. - The root
package.jsonworkspace array (lines 6–13) explicitly orders packages from low-level utilities (page-controller, llms) to high-level aggregates (page-agent). - Each package maintains isolated build configurations (primarily Vite), producing separate distributable artifacts while sharing devDependencies through hoisting.
- Import statements remain consistent across the repository and for external consumers, as all internal modules use the scoped
@page-agent/namespace.
Frequently Asked Questions
How does npm resolve local workspace dependencies without publishing to the registry?
Npm resolves the @page-agent/* specifiers to the local file system using the workspace protocol defined in the root package.json. When you run npm install at the repository root, npm creates symlinks from node_modules/@page-agent/core to packages/core, allowing Node.js module resolution to find the local source code as if it were a published package.
Why does the order of workspaces in the root package.json matter?
The workspaces array follows a topological sort where foundational packages like @page-agent/page-controller appear before dependent packages like @page-agent/core. This ordering ensures that during the initial installation and build phases, npm can link upstream packages before downstream packages attempt to import them, preventing "module not found" errors in CI pipelines.
Can I use individual Page-Agent packages in my own project without installing the entire monorepo?
Yes. Because each package uses the scoped npm namespace (@page-agent/core, @page-agent/llms, etc.), they can be published to the registry independently and consumed separately. The architecture intentionally separates concerns—allowing you to import just the headless core agent without the React-based UI components if you only need programmatic browser automation.
What build tool does the Page-Agent monorepo use to compile TypeScript?
The majority of packages use Vite as their build tool, configured individually in each workspace’s vite.config.ts. The root package.json orchestrates these builds via the build:libs script, which runs npm run build in each workspace in dependency order, ensuring that type definitions and JavaScript bundles propagate correctly through the internal dependency chain.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →