@agentpm/sdk
v0.1.5
Published
AgentPM™ Node SDK
Downloads
421
Readme
AgentPM™ Node SDK
A lean, framework-agnostic Node.js SDK for running AgentPM tools and inspecting installed agent packages from your app or agent runtime.
- 🔎 Discovers tools installed by
agentpm installin.agentpm/tools(project) and~/.agentpm/tools(user), withAGENTPM_TOOL_DIRoverride. - 📦 Loads installed agents from
.agentpm/agentsand exposes their resolved tool refs fromagent.lock. - 🚀 Executes entrypoints in a subprocess (
node/python) and exchanges JSON over stdin/stdout. - 🧩 Metadata-aware:
withMetareturnsfunc + meta(name, version, description, inputs, outputs). - 🧪 Adapters: tiny helpers (e.g. LangChain) without forcing extra deps.
Requires Node 20+. Ships ESM + CJS builds. Types included.
Installation
Using pnpm (recommended):
pnpm add @agentpm/sdkWith npm:
npm i @agentpm/sdkWith yarn:
yarn add @agentpm/sdkIf you'll use the optional LangChain adapter:
pnpm add @langchain/core
# npm i @langchain/core
# yarn add @langchain/coreQuick Start
Minimal usage
import { load } from '@agentpm/sdk';
// spec format: "@scope/name@version"
const summarize = await load('@zack/[email protected]');
const result = await summarize({ text: 'Long document content...' });
console.log(result.summary);With metadata (build richer tool descriptions)
import { load, toLangChainTool } from '@agentpm/sdk';
const loaded = await load('@zack/[email protected]', { withMeta: true });
const { func: summarize, meta } = loaded;
const richDescription =
`${meta.description ?? ''} ` +
`Inputs: ${JSON.stringify(meta.inputs)}. ` +
`Outputs: ${JSON.stringify(meta.outputs)}.`;
console.log(richDescription);
console.log((await summarize({ text: 'hello' })).summary);
// Optional: turn it into a LangChain tool (requires @langchain/core)
const lcTool = await toLangChainTool(loaded);Load an installed agent package
import { load, loadAgent } from '@agentpm/sdk';
const agent = await loadAgent('@zack/[email protected]');
const firstTool = agent.resolvedTools[0];
const tool = await load(`${firstTool.name}@${firstTool.version}`);loadAgent() returns:
- the installed agent manifest
- the installed agent root path
- reserved refs (
skills,knowledge,memory,profiles) as metadata resolvedToolsfromagent.lockv2
It does not execute the agent package or orchestrate the tools for you.
CJS require
const { load } = require('@agentpm/sdk');Where tools are discovered
Resolution order:
AGENTPM_TOOL_DIR(environment variable)./.agentpm/tools(project-local)~/.agentpm/tools(user-local)
Each tool lives in a directory like:
.agentpm/
tools/
@zack/summarize/
0.1.0/
agent.json
(tool files…)Installed registry agent packages live separately:
.agentpm/
agents/
@zack/support-agent/
0.1.0/
agent.json
README.mdWhere installed agents are discovered
Resolution order for loadAgent():
AGENTPM_AGENT_DIR(environment variable)./.agentpm/agents(project-local)~/.agentpm/agents(user-local)
You can also override per call:
await loadAgent('@zack/[email protected]', {
agentDirOverride: '/path/to/agents',
});Manifest & Runtime Contract
agent.json (minimal fields used by the SDK):
{
"name": "@zack/summarize",
"version": "0.1.0",
"description": "Summarize long text.",
"inputs": {
"type": "object",
"properties": { "text": { "type": "string", "description": "Text to summarize" } },
"required": ["text"]
},
"outputs": {
"type": "object",
"properties": { "summary": { "type": "string", "description": "Summarized text" } },
"required": ["summary"]
},
"entrypoint": {
"command": "node",
"args": ["dist/cli.js"],
"cwd": ".",
"timeout_ms": 60000,
"env": {}
}
}Execution contract:
- SDK writes inputs JSON to the process stdin.
- Tool writes a single outputs JSON object to stdout.
- Non-JSON logs should go to stderr.
- Process must exit with code 0 on success.
Interpreter whitelist: node, nodejs, python, python3.
The SDK validates the interpreter and checks it’s present on PATH.
TypeScript & Module Formats
- ESM import:
import { load } from "@agentpm/sdk"→ loadsdist/index.js(ESM). - CJS require:
const { load } = require("@agentpm/sdk")→ loadsdist/index.cjs(CJS). - Types:
dist/index.d.ts.
If you see
ERR_MODULE_NOT_FOUNDforindex.mjs, ensure your package.json maps the exports todist/index.js(ESM) anddist/index.cjs(CJS).
Development (this repo)
pnpm i
# build / test / lint / format
pnpm build
pnpm test
pnpm lint
pnpm format
# dev watch
pnpm devPre-commit hooks (Husky v9+)
pnpm dlx husky@latest init
printf 'pnpm lint-staged
' > .husky/pre-commit
chmod +x .husky/pre-commitpackage.json essentials:
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs",
},
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"lint": "eslint . --max-warnings 0",
"format": "prettier --check .",
},
}ESLint v9 (flat config)
Create eslint.config.mjs:
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default tseslint.config(
{ ignores: ['dist/**'] },
js.configs.recommended,
...tseslint.configs.recommended,
prettier,
);Add
@types/nodein dev deps for TypeScript Node built-ins:pnpm add -D @types/node
Optional: LangChain adapter
This SDK provides toLangChainTool(loaded, ...) which returns a dynamic tool. We keep @langchain/core as an optional peer. Install it only if you need the adapter:
pnpm add @langchain/coreIn code:
import { load, toLangChainTool } from '@agentpm/sdk';
const loaded = await load('@zack/[email protected]', { withMeta: true });
const tool = await toLangChainTool(loaded);Publishing
# ensure your exports map and files list are correct
pnpm install
pnpm test && pnpm build
npm pack # confirm the tarball contents
npm publish --dry-run # double-check what would be published
# publish (scoped packages default to private without access flag)
pnpm publish --access publicOptional in package.json:
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}Running mixed-runtime Agent apps with Docker
Some AgentPM tools run on Node, some on Python—and your agent may need to spawn both. Using Docker gives you a single, reproducible environment where both interpreters are installed and on PATH, which avoids the common “interpreter not found” issues that pop up on PaaS/CI or IDEs.
Why Docker?
✅ Hermetic: Python + Node versions are pinned inside the image.
✅ No PATH drama: node/python are present and discoverable.
✅ Prod/CI parity: the same image runs on your laptop, CI, and servers.
✅ Easy secrets: pass API keys via env at docker run/Compose time.
✅ Fewer surprises: consistent OS libs for LLM clients, SSL, etc.
When to use it
- You deploy to platforms that don’t let you apt-get both runtimes.
- Your agent uses tools with different interpreters (Node + Python).
- Your local dev/IDE PATH differs from production and causes failures.
- You want reproducible builds and easy rollback.
How to use it
- Copy the provided Dockerfile into your repo.
- (Optional) Pre-install tools locally with agentpm install ... and commit or copy .agentpm/tools/ into the image, or run agentpm install at build time if your CLI is available in the image.
- Build & run:
docker build -t agent-app .
docker run --rm -e OPENAI_API_KEY=$OPENAI_API_KEY agent-app- For development, use the docker-compose.yml snippet to mount your source and pass env vars conveniently.
Troubleshooting
- Set
AGENTPM_DEBUG=1to print the SDK’s project root, search paths, merged PATH, and resolved interpreters. - You can force interpreters via:
AGENTPM_NODE=/usr/bin/node
AGENTPM_PYTHON=/usr/local/bin/python3.11- Prefer absolute interpreters in agent.json.entrypoint.command for production (e.g., /usr/bin/node). The SDKs still enforce the Node/Python family.
Troubleshooting
ERR_MODULE_NOT_FOUND for ESM Your
package.jsonlikely points todist/index.mjsbut your build emitsdist/index.js. Fix theexportsmap or configure tsup to output.mjs.Husky "add is DEPRECATED" In v9+, use
husky initand write hook files directly (see above).ESLint can't find config ESLint v9 uses flat config. Create
eslint.config.mjsas shown.Types for
@langchain/coreKeep@langchain/coreas a peer (optional). For build-time types in this repo, install it as a devDependency and mark itexternalintsup.config.ts.
License
MIT — see LICENSE.
