feat(create-langgraph): allow user to generate a langgraph config file (#1833)

This commit is contained in:
Christian Bromann
2026-01-05 11:54:44 -08:00
committed by GitHub
parent 97e7210289
commit 074da7ffaa
12 changed files with 1665 additions and 42 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"create-langgraph": minor
---
feat(create-langgraph): allow user to generate a langgraph config file
+216
View File
@@ -0,0 +1,216 @@
# create-langgraph
[![npm version](https://img.shields.io/npm/v/create-langgraph.svg)](https://www.npmjs.com/package/create-langgraph)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
The official scaffolding tool for [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) projects. Quickly bootstrap new LangGraph applications from curated templates or generate configuration files for existing projects.
## Quick Start
Create a new LangGraph project with a single command:
```bash
# Using npm
npm init langgraph@latest
# Using yarn
yarn create langgraph
# Using pnpm
pnpm create langgraph
# Using bun
bunx create-langgraph
```
Follow the interactive prompts to select a template and configure your project.
## Templates
Choose from a variety of production-ready templates:
| Template | Description |
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| [**New LangGraph Project**](https://github.com/langchain-ai/new-langgraphjs-project) | A simple, minimal chatbot with memory |
| [**ReAct Agent**](https://github.com/langchain-ai/react-agent-js) | A flexible agent that can be extended with many tools |
| [**Memory Agent**](https://github.com/langchain-ai/memory-agent-js) | A ReAct-style agent with persistent memory across conversations |
| [**Retrieval Agent**](https://github.com/langchain-ai/retrieval-agent-template-js) | An agent with retrieval-based question-answering |
| [**Data-enrichment Agent**](https://github.com/langchain-ai/data-enrichment-js) | An agent that performs web searches and organizes findings |
### Using a Specific Template
Skip the interactive prompt by specifying a template directly:
```bash
npx create-langgraph@latest my-project --template react-agent-js
```
Available template IDs:
- `new-langgraph-project-js`
- `react-agent-js`
- `memory-agent-js`
- `retrieval-agent-js`
- `data-enrichment-js`
## Commands
### `create-langgraph [path]`
Creates a new LangGraph project at the specified path.
```bash
npx create-langgraph@latest my-awesome-agent
```
**Options:**
- `-t, --template <template>` — Use a specific template (skips interactive selection)
**What it does:**
1. Downloads the selected template from GitHub
2. Extracts it to your target directory
3. Optionally initializes a Git repository
4. Provides next steps for getting started
### `create-langgraph config [path]`
Scans your project for LangGraph agents and generates a `langgraph.json` configuration file.
```bash
# In your project directory
npx create-langgraph@latest config
# Or specify a path
npx create-langgraph@latest config ./my-project
```
This command is useful when:
- You have an existing project and want to add LangGraph Platform support
- You've added new agents and need to update your configuration
- You want to automatically detect all agents in your codebase
## Agent Detection
The `config` command automatically detects LangGraph agents defined using these patterns:
### ESM (ES Modules)
```typescript
// Using createAgent
export const agent = createAgent({ model, tools });
// Using StateGraph
export const graph = new StateGraph(annotation).compile();
// Using workflow builder pattern
export const app = workflow.compile();
```
### CommonJS
```javascript
// Using module.exports
module.exports.agent = createAgent({ model, tools });
module.exports.graph = workflow.compile();
// Using exports shorthand
exports.myAgent = createAgent({ model, tools });
```
### What Gets Detected
The scanner looks for:
- `createAgent()` function calls
- `new StateGraph(...).compile()` patterns
- `workflow.compile()` or `builder.compile()` patterns
**Important:** Only **exported** agents are included in the generated configuration. Unexported agents will be listed as warnings so you can add the `export` keyword if needed.
## Generated Configuration
The `config` command generates a `langgraph.json` file like this:
```json
{
"node_version": "20",
"graphs": {
"agent": "./src/agent.ts:agent",
"searchAgent": "./src/search.ts:searchAgent"
},
"env": ".env"
}
```
The configuration includes:
- **node_version** — Detected from your current Node.js version
- **graphs** — Map of agent names to their file paths and export names
- **env** — Path to `.env` file (if one exists)
## Project Structure
After scaffolding, your project will have this structure:
```txt
my-project/
├── src/
│ └── agent.ts # Your LangGraph agent
├── langgraph.json # LangGraph configuration
├── package.json
├── tsconfig.json
└── .env.example # Environment variables template
```
## Next Steps After Creating a Project
```bash
# Navigate to your project
cd my-project
# Install dependencies
npm install # or yarn, pnpm, bun
# Start the LangGraph development server
npx @langchain/langgraph-cli@latest dev
```
The development server provides:
- A local API server for your agents
- Hot reloading during development
- Built-in debugging tools
## Analytics
This CLI collects anonymous usage analytics to help improve the tool. The following information is collected:
- Operating system and version
- Node.js version
- CLI version
- Command executed
**No personal information, project details, or code is ever collected.**
To opt out of analytics, set the environment variable:
```bash
export LANGGRAPH_CLI_NO_ANALYTICS=1
```
## Requirements
- Node.js 18 or later
- npm, yarn, pnpm, or bun
## Related Packages
- [@langchain/langgraph](https://www.npmjs.com/package/@langchain/langgraph) — The core LangGraph library
- [@langchain/langgraph-cli](https://www.npmjs.com/package/@langchain/langgraph-cli) — CLI tools for running LangGraph projects
## License
MIT © [LangChain](https://langchain.com)
+8 -5
View File
@@ -3,8 +3,9 @@
"version": "1.0.0",
"description": "Create a new LangGraph project",
"license": "MIT",
"main": "dist/index.mjs",
"bin": "dist/cli.mjs",
"main": "dist/index.js",
"bin": "dist/cli.js",
"type": "module",
"files": [
"dist"
],
@@ -20,6 +21,8 @@
"build": "yarn turbo:command build:internal --filter=create-langgraph",
"build:internal": "yarn clean && yarn tsc --outDir dist",
"prepublish": "yarn build",
"test": "vitest run",
"test:watch": "vitest",
"format": "prettier --write .",
"format:check": "prettier --check ."
},
@@ -36,12 +39,12 @@
"prettier": "^2.8.3",
"tsx": "^4.19.3",
"typescript": "^4.9.5 || ^5.4.5",
"vitest": "^3.2.4"
"vitest": "^4.0.16"
},
"exports": {
".": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"keywords": []
-21
View File
@@ -1,21 +0,0 @@
#!/usr/bin/env node
import { Command } from "@commander-js/extra-typings";
import { version } from "./utils/version.mjs";
import { withAnalytics } from "./utils/analytics.mjs";
import { createNew } from "./index.mjs";
const program = new Command()
.name("create-langgraph")
.version(version)
.description("Create a new LangGraph project")
.argument("[path]", "Path to create the project")
.option("-t, --template <template>", "Template to use", "")
.hook("preAction", withAnalytics())
.action((path, options) => {
createNew(path, options.template).catch((error) => {
console.error("Error:", error.message);
process.exit(1);
});
});
program.parse();
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env node
import { Command } from "@commander-js/extra-typings";
import { version } from "./utils/version.js";
import { withAnalytics } from "./utils/analytics.js";
import { createNew } from "./index.js";
import { generateConfig } from "./config.js";
const program = new Command()
.name("create-langgraph")
.version(version)
.description("Create a new LangGraph project");
// Default command: create a new project
program
.argument("[path]", "Path to create the project")
.option("-t, --template <template>", "Template to use", "")
.hook("preAction", withAnalytics())
.action((path, options) => {
createNew(path, options.template).catch((error) => {
console.error("Error:", error.message);
process.exit(1);
});
});
// Config subcommand: generate langgraph.json
program
.command("config")
.description(
"Generate a langgraph.json configuration file by scanning for agents"
)
.argument(
"[path]",
"Path to the project to scan (defaults to current directory)"
)
.hook("preAction", withAnalytics())
.action((path) => {
generateConfig(path).catch((error) => {
console.error("Error:", error.message);
process.exit(1);
});
});
program.parse();
+350
View File
@@ -0,0 +1,350 @@
import fs from "node:fs/promises";
import path from "node:path";
import {
intro,
outro,
spinner,
confirm,
isCancel,
cancel,
} from "@clack/prompts";
import color from "picocolors";
import dedent from "dedent";
export interface AgentInfo {
name: string;
filePath: string;
isExported: boolean;
lineNumber: number;
}
interface LangGraphConfig {
node_version?: string;
graphs: Record<string, string>;
env?: string;
}
// Pattern strings for detecting LangGraph agents (will create fresh RegExp for each file)
// Note: Using [a-zA-Z_$][\\w$]* to match valid JS identifiers (including $ prefix)
export const AGENT_PATTERN_STRINGS = [
// ESM: createAgent
"(?:export\\s+)?(?:const|let|var)\\s+([a-zA-Z_$][\\w$]*)\\s*=\\s*(?:await\\s+)?createAgent\\s*\\(",
// ESM: StateGraph().compile() or workflow.compile()
"(?:export\\s+)?(?:const|let|var)\\s+([a-zA-Z_$][\\w$]*)\\s*=\\s*(?:await\\s+)?(?:new\\s+StateGraph\\s*\\([^)]*\\)|[a-zA-Z_$][\\w$]*)\\.compile\\s*\\(",
// CJS: module.exports.name = createAgent(...) or exports.name = createAgent(...)
"(?:module\\.)?exports\\.([a-zA-Z_$][\\w$]*)\\s*=\\s*(?:await\\s+)?createAgent\\s*\\(",
// CJS: module.exports.name = workflow.compile(...) or exports.name = workflow.compile(...)
"(?:module\\.)?exports\\.([a-zA-Z_$][\\w$]*)\\s*=\\s*(?:await\\s+)?(?:new\\s+StateGraph\\s*\\([^)]*\\)|[a-zA-Z_$][\\w$]*)\\.compile\\s*\\(",
];
// Pattern to check if it's an ESM export
export const ESM_EXPORT_PATTERN = /^export\s+/;
// Pattern to check if it's a CJS export
export const CJS_EXPORT_PATTERN = /^(?:module\.)?exports\./;
/**
* Scan content string for LangGraph agent definitions
* Exported for testing purposes
*/
export function scanContentForAgents(
content: string,
filePath: string = "test.ts"
): AgentInfo[] {
const agents: AgentInfo[] = [];
const lines = content.split("\n");
for (const patternString of AGENT_PATTERN_STRINGS) {
// Create a fresh RegExp for each pattern to avoid lastIndex issues
const pattern = new RegExp(patternString, "g");
let match;
while ((match = pattern.exec(content)) !== null) {
const name = match[1];
const matchIndex = match.index;
// Find the line number
let lineNumber = 1;
let charCount = 0;
for (const line of lines) {
if (charCount + line.length >= matchIndex) {
break;
}
charCount += line.length + 1; // +1 for newline
lineNumber++;
}
// Check if it's exported (ESM or CJS)
const isExported =
ESM_EXPORT_PATTERN.test(match[0]) || CJS_EXPORT_PATTERN.test(match[0]);
// Avoid duplicates
if (!agents.find((a) => a.name === name && a.lineNumber === lineNumber)) {
agents.push({
name,
filePath,
isExported,
lineNumber,
});
}
}
}
return agents;
}
/**
* Scan a file for LangGraph agent definitions
*/
async function scanFileForAgents(filePath: string): Promise<AgentInfo[]> {
try {
const content = await fs.readFile(filePath, "utf-8");
return scanContentForAgents(content, filePath);
} catch (error) {
// Skip files that can't be read
return [];
}
}
/**
* Recursively find all TypeScript and JavaScript files in a directory
*/
async function findTsJsFiles(
dir: string,
files: string[] = []
): Promise<string[]> {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
// Skip node_modules, dist, .git, etc.
if (
entry.isDirectory() &&
!["node_modules", "dist", ".git", ".turbo", "build", "coverage"].includes(
entry.name
)
) {
await findTsJsFiles(fullPath, files);
} else if (
entry.isFile() &&
/\.(ts|tsx|mts|js|jsx|mjs)$/.test(entry.name) &&
!entry.name.endsWith(".d.ts") &&
!entry.name.endsWith(".test.ts") &&
!entry.name.endsWith(".spec.ts")
) {
files.push(fullPath);
}
}
return files;
}
/**
* Generate the langgraph.json configuration
*/
export async function generateConfig(targetPath?: string) {
const targetRootPath = targetPath ? path.resolve(targetPath) : process.cwd();
intro(`${color.bgCyan(color.black(" 🦜 create-langgraph config "))}`);
// Check if langgraph.json already exists
const configPath = path.join(targetRootPath, "langgraph.json");
try {
await fs.access(configPath);
const overwrite = await confirm({
message: `${color.yellow(
"langgraph.json"
)} already exists. Do you want to overwrite it?`,
initialValue: false,
});
if (isCancel(overwrite) || !overwrite) {
cancel("Operation cancelled");
process.exit(0);
}
} catch {
// File doesn't exist, continue
}
const s = spinner();
s.start(`Scanning for LangGraph agents in ${targetRootPath}...`);
// Find all TS/JS files
let files: string[];
try {
files = await findTsJsFiles(targetRootPath);
} catch (error) {
s.stop("Error scanning directory");
throw new Error(`Failed to scan directory: ${(error as Error).message}`);
}
if (files.length === 0) {
s.stop("No TypeScript or JavaScript files found");
outro(
color.yellow(
"No TypeScript or JavaScript files found in the current directory."
)
);
return;
}
// Scan all files for agents
const allAgents: AgentInfo[] = [];
for (const file of files) {
const agents = await scanFileForAgents(file);
allAgents.push(...agents);
}
s.stop(`Found ${allAgents.length} agent(s) in ${files.length} file(s)`);
if (allAgents.length === 0) {
outro(
color.yellow(
dedent`
No LangGraph agents found.
Make sure your agents are defined using one of these patterns:
- ${color.cyan("createAgent({ ... })")}
- ${color.cyan("createReactAgent({ ... })")}
- ${color.cyan("new StateGraph(...).compile()")}
- ${color.cyan("workflow.compile()")}
`
)
);
return;
}
// Separate exported and unexported agents
const exportedAgents = allAgents.filter((a) => a.isExported);
const unexportedAgents = allAgents.filter((a) => !a.isExported);
// Warn about unexported agents
if (unexportedAgents.length > 0) {
console.log();
console.log(
color.yellow(
`⚠️ Found ${unexportedAgents.length} agent(s) that are not exported:`
)
);
for (const agent of unexportedAgents) {
const relativePath = path.relative(targetRootPath, agent.filePath);
console.log(
color.dim(
`${color.white(agent.name)} at ${relativePath}:${
agent.lineNumber
}`
)
);
}
console.log(
color.dim(
` Add ${color.cyan(
"export"
)} keyword to include them in the configuration.`
)
);
console.log();
}
if (exportedAgents.length === 0) {
outro(
color.yellow(
dedent`
No exported agents found.
To include an agent in the configuration, make sure it's exported:
${color.cyan("export const agent = createAgent({ ... });")}
`
)
);
return;
}
// Build the graphs config
const graphs: Record<string, string> = {};
const usedKeys = new Set<string>();
for (const agent of exportedAgents) {
const relativePath = path.relative(targetRootPath, agent.filePath);
// Use ./ prefix for relative paths
const normalizedPath = relativePath.startsWith(".")
? relativePath
: `./${relativePath}`;
// Generate a unique key for the graph
let key = agent.name;
// If the key is already used, derive from the directory or file name
if (usedKeys.has(key)) {
// Try to use the parent directory name
const dirName = path.basename(path.dirname(agent.filePath));
if (dirName && dirName !== "." && !usedKeys.has(dirName)) {
key = dirName;
} else {
// Use filename without extension as fallback
const fileName = path
.basename(agent.filePath)
.replace(/\.(ts|tsx|mts|js|jsx|mjs)$/, "");
key = `${fileName}-${agent.name}`;
// If still a collision, add a numeric suffix
let suffix = 1;
let uniqueKey = key;
while (usedKeys.has(uniqueKey)) {
uniqueKey = `${key}-${suffix}`;
suffix++;
}
key = uniqueKey;
}
}
usedKeys.add(key);
graphs[key] = `${normalizedPath}:${agent.name}`;
}
// Detect Node.js version
const nodeVersion = process.version.replace(/^v/, "").split(".")[0];
// Check if .env file exists
let envPath: string | undefined;
try {
await fs.access(path.join(targetRootPath, ".env"));
envPath = ".env";
} catch {
// .env doesn't exist
}
// Create the config
const config: LangGraphConfig = {
node_version: nodeVersion,
graphs,
};
if (envPath) {
config.env = envPath;
}
// Write the config file
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
// Summary
console.log();
console.log(color.green("✓ Created langgraph.json with:"));
for (const [name, graphPath] of Object.entries(graphs)) {
console.log(color.dim(`${color.white(name)}: ${graphPath}`));
}
console.log();
outro(
dedent`
${color.green("Configuration created successfully!")}
${color.cyan("Next steps:")}
- Review the generated ${color.yellow("langgraph.json")}
- Run ${color.cyan(
"npx @langchain/langgraph-cli@latest dev"
)} to start the development server
`
);
}
@@ -1,3 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import cp from "node:child_process";
import {
intro,
outro,
@@ -7,12 +11,9 @@ import {
cancel,
confirm,
} from "@clack/prompts";
import * as fs from "fs/promises";
import * as path from "path";
import zipExtract from "extract-zip";
import color from "picocolors";
import dedent from "dedent";
import { spawn } from "child_process";
const TEMPLATES = {
"New LangGraph Project": {
@@ -221,7 +222,7 @@ export async function createNew(projectPath?: string, templateId?: string) {
if (shouldInitGit) {
await new Promise((resolve, reject) => {
const proc = spawn("git", ["init"], { cwd: absolutePath });
const proc = cp.spawn("git", ["init"], { cwd: absolutePath });
proc.on("close", (code) => {
if (code === 0) resolve(undefined);
else reject(new Error(`git init failed with code ${code}`));
@@ -0,0 +1,494 @@
import { describe, it, expect } from "vitest";
import {
scanContentForAgents,
ESM_EXPORT_PATTERN,
CJS_EXPORT_PATTERN,
} from "../config.js";
describe("Agent Detection Patterns", () => {
describe("ESM: createAgent", () => {
it("should detect exported createAgent with const", () => {
const content = `export const agent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(true);
});
it("should detect exported createAgent with let", () => {
const content = `export let myAgent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("myAgent");
expect(agents[0].isExported).toBe(true);
});
it("should detect exported createAgent with var", () => {
const content = `export var legacyAgent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("legacyAgent");
expect(agents[0].isExported).toBe(true);
});
it("should detect unexported createAgent", () => {
const content = `const agent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(false);
});
it("should detect createAgent with await", () => {
const content = `export const agent = await createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(true);
});
it("should detect createAgent with extra whitespace", () => {
const content = `export const agent = createAgent ( { model, tools } );`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
});
it("should detect createAgent with multiline definition", () => {
const content = `export const agent = createAgent({
model,
tools: [tool1, tool2],
systemPrompt: "You are helpful"
});`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
});
it("should detect multiple createAgent definitions", () => {
const content = `
export const agent1 = createAgent({ model, tools });
export const agent2 = createAgent({ model, tools });
const privateAgent = createAgent({ model, tools });
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(3);
expect(agents.filter((a) => a.isExported)).toHaveLength(2);
expect(agents.filter((a) => !a.isExported)).toHaveLength(1);
});
it("should handle various agent naming conventions", () => {
const names = [
"agent",
"myAgent",
"my_agent",
"Agent1",
"_privateAgent",
"$specialAgent",
"AGENT",
"weatherAgent",
"chatBot",
];
for (const name of names) {
const content = `export const ${name} = createAgent({ model });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe(name);
}
});
});
describe("ESM: StateGraph.compile()", () => {
it("should detect new StateGraph().compile()", () => {
const content = `export const graph = new StateGraph(annotation).compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
expect(agents[0].isExported).toBe(true);
});
it("should detect workflow.compile() pattern", () => {
const content = `export const agent = workflow.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(true);
});
it("should detect builder.compile() pattern", () => {
const content = `export const graph = builder.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
});
it("should detect unexported .compile()", () => {
const content = `const graph = workflow.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
expect(agents[0].isExported).toBe(false);
});
it("should detect .compile() with options", () => {
const content = `export const agent = workflow.compile({ checkpointer: new MemorySaver() });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
});
it("should detect await .compile()", () => {
const content = `export const graph = await workflow.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
});
it("should detect new StateGraph with complex annotation", () => {
const content = `export const app = new StateGraph(StateAnnotation).compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("app");
});
it("should detect chained StateGraph methods before compile", () => {
const content = `
const workflow = new StateGraph(annotation)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addEdge("agent", "tools");
export const graph = workflow.compile();
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
expect(agents[0].isExported).toBe(true);
});
});
describe("CJS: module.exports", () => {
it("should detect module.exports.name = createAgent()", () => {
const content = `module.exports.agent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(true);
});
it("should detect exports.name = createAgent()", () => {
const content = `exports.myAgent = createAgent({ model, tools });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("myAgent");
expect(agents[0].isExported).toBe(true);
});
it("should detect module.exports.name = workflow.compile()", () => {
const content = `module.exports.graph = workflow.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
expect(agents[0].isExported).toBe(true);
});
it("should detect exports.name = workflow.compile()", () => {
const content = `exports.app = builder.compile();`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("app");
expect(agents[0].isExported).toBe(true);
});
it("should detect multiple CJS exports", () => {
const content = `
module.exports.agent1 = createAgent({ model });
exports.agent2 = createAgent({ model });
module.exports.graph = workflow.compile();
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(3);
expect(agents.every((a) => a.isExported)).toBe(true);
});
});
describe("Mixed ESM and CJS in same file", () => {
it("should detect both ESM and CJS patterns", () => {
const content = `
// ESM style
export const esmAgent = createAgent({ model });
// CJS style (unusual but possible in some setups)
module.exports.cjsAgent = createAgent({ model });
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(2);
expect(agents.find((a) => a.name === "esmAgent")).toBeDefined();
expect(agents.find((a) => a.name === "cjsAgent")).toBeDefined();
});
});
describe("Line number detection", () => {
it("should correctly detect line numbers", () => {
const content = `import { createAgent } from "langchain";
const model = new ChatOpenAI();
export const agent = createAgent({
model,
tools,
});
export const secondAgent = createAgent({ model });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(2);
expect(agents[0].lineNumber).toBe(5);
expect(agents[1].lineNumber).toBe(10);
});
it("should handle Windows line endings (CRLF)", () => {
const content = `import { createAgent } from "langchain";\r\n\r\nexport const agent = createAgent({ model });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
// Note: Line count may differ with CRLF, but agent should still be found
expect(agents[0].name).toBe("agent");
});
});
describe("Edge cases and false positives", () => {
it("should not detect createAgent in comments", () => {
const content = `
// export const agent = createAgent({ model });
/* export const agent = createAgent({ model }); */
`;
const agents = scanContentForAgents(content);
// Regex doesn't handle comments specially, so these might still match
// This is a known limitation - documenting current behavior
expect(agents.length).toBeGreaterThanOrEqual(0);
});
it("should not detect createAgent in strings", () => {
const content = `
const example = "export const agent = createAgent({ model });";
`;
const agents = scanContentForAgents(content);
// Regex doesn't handle strings specially - documenting current behavior
expect(agents.length).toBeGreaterThanOrEqual(0);
});
it("should handle empty content", () => {
const content = "";
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(0);
});
it("should handle content with no agents", () => {
const content = `
import { something } from "somewhere";
export function myFunction() {
return "hello";
}
export const value = 42;
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(0);
});
it("should not match partial function names", () => {
const content = `
export const agent = myCreateAgent({ model });
export const graph = createAgentExecutor({ model });
`;
const agents = scanContentForAgents(content);
// Should not match myCreateAgent or createAgentExecutor
expect(agents).toHaveLength(0);
});
it("should handle createAgent from different imports", () => {
const content = `
import { createAgent } from "langchain";
import { createAgent as customAgent } from "custom-lib";
export const agent1 = createAgent({ model });
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent1");
});
});
describe("Real-world code patterns", () => {
it("should detect agent in typical TypeScript file", () => {
const content = `
import { createAgent, tool } from "langchain";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const getWeather = tool(
async ({ location }) => {
return \`Weather in \${location}: Sunny\`;
},
{
name: "get_weather",
description: "Get weather for a location",
schema: z.object({
location: z.string(),
}),
}
);
export const agent = createAgent({
model,
tools: [getWeather],
systemPrompt: "You are a helpful assistant.",
});
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("agent");
expect(agents[0].isExported).toBe(true);
});
it("should detect compiled StateGraph in typical file", () => {
const content = `
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
const StateAnnotation = Annotation.Root({
messages: Annotation<string[]>({
reducer: (left, right) => left.concat(right),
default: () => [],
}),
});
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
async function agentNode(state: typeof StateAnnotation.State) {
const response = await model.invoke(state.messages);
return { messages: [response.content] };
}
const workflow = new StateGraph(StateAnnotation)
.addNode("agent", agentNode)
.addEdge(START, "agent")
.addEdge("agent", END);
export const graph = workflow.compile();
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].name).toBe("graph");
expect(agents[0].isExported).toBe(true);
});
it("should detect multiple agents in a file with both patterns", () => {
const content = `
import { createAgent } from "langchain";
import { StateGraph, START, END } from "@langchain/langgraph";
// Simple agent using createAgent
export const simpleAgent = createAgent({
model,
tools: [searchTool],
});
// Complex workflow using StateGraph
const complexWorkflow = new StateGraph(StateAnnotation)
.addNode("research", researchNode)
.addNode("write", writeNode)
.addEdge(START, "research")
.addEdge("research", "write")
.addEdge("write", END);
export const complexAgent = complexWorkflow.compile();
// Private agent (not exported)
const internalAgent = createAgent({ model, tools: [] });
`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(3);
expect(agents.filter((a) => a.isExported)).toHaveLength(2);
expect(agents.filter((a) => !a.isExported)).toHaveLength(1);
expect(agents.find((a) => a.name === "simpleAgent")).toBeDefined();
expect(agents.find((a) => a.name === "complexAgent")).toBeDefined();
expect(agents.find((a) => a.name === "internalAgent")).toBeDefined();
});
it("should detect CJS module in CommonJS file", () => {
const content = `
const { createAgent } = require("langchain");
const { ChatOpenAI } = require("@langchain/openai");
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const agent = createAgent({
model,
tools: [],
});
module.exports.agent = agent;
module.exports.weatherAgent = createAgent({
model,
tools: [weatherTool],
});
`;
const agents = scanContentForAgents(content);
// Should find the unexported 'agent' and the exported 'weatherAgent'
expect(
agents.find((a) => a.name === "agent" && !a.isExported)
).toBeDefined();
expect(
agents.find((a) => a.name === "weatherAgent" && a.isExported)
).toBeDefined();
});
});
describe("FilePath handling", () => {
it("should include filePath in agent info", () => {
const content = `export const agent = createAgent({ model });`;
const agents = scanContentForAgents(content, "/path/to/agent.ts");
expect(agents).toHaveLength(1);
expect(agents[0].filePath).toBe("/path/to/agent.ts");
});
it("should use default filePath when not provided", () => {
const content = `export const agent = createAgent({ model });`;
const agents = scanContentForAgents(content);
expect(agents).toHaveLength(1);
expect(agents[0].filePath).toBe("test.ts");
});
});
});
describe("Export Pattern Regexes", () => {
describe("ESM_EXPORT_PATTERN", () => {
it("should match ESM export statements", () => {
expect(ESM_EXPORT_PATTERN.test("export const x")).toBe(true);
expect(ESM_EXPORT_PATTERN.test("export let x")).toBe(true);
expect(ESM_EXPORT_PATTERN.test("export var x")).toBe(true);
expect(ESM_EXPORT_PATTERN.test("export function")).toBe(true);
});
it("should not match non-export statements", () => {
expect(ESM_EXPORT_PATTERN.test("const x = export")).toBe(false);
expect(ESM_EXPORT_PATTERN.test("module.exports")).toBe(false);
expect(ESM_EXPORT_PATTERN.test("exports.x")).toBe(false);
});
});
describe("CJS_EXPORT_PATTERN", () => {
it("should match CJS export statements", () => {
expect(CJS_EXPORT_PATTERN.test("module.exports.x")).toBe(true);
expect(CJS_EXPORT_PATTERN.test("exports.x")).toBe(true);
});
it("should not match non-export statements", () => {
expect(CJS_EXPORT_PATTERN.test("export const x")).toBe(false);
expect(CJS_EXPORT_PATTERN.test("const exports")).toBe(false);
});
});
});
@@ -1,7 +1,9 @@
import os from "node:os";
import { version } from "./version.mjs";
import type { Command } from "@commander-js/extra-typings";
import { version } from "./version.js";
const SUPABASE_PUBLIC_API_KEY =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt6cmxwcG9qaW5wY3l5YWlweG5iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTkyNTc1NzksImV4cCI6MjAzNDgzMzU3OX0.kkVOlLz3BxemA5nP-vat3K4qRtrDuO4SwZSR_htcX9c";
const SUPABASE_URL = "https://kzrlppojinpcyyaipxnb.supabase.co";
@@ -1,5 +1,5 @@
import * as fs from "node:fs/promises";
import * as url from "node:url";
import fs from "node:fs/promises";
import url from "node:url";
async function getVersion() {
try {
+6 -7
View File
@@ -1,16 +1,15 @@
{
"compilerOptions": {
"outDir": "dist",
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"module": "NodeNext",
"allowJs": true,
"verbatimModuleSyntax": true,
"noUnusedLocals": true,
"declaration": true,
"outDir": "dist"
"declaration": true
},
"exclude": ["scripts/*", "dist/*"]
"exclude": ["scripts/*", "dist/*", "src/tests/*"]
}
+532 -2
View File
@@ -584,6 +584,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/aix-ppc64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/aix-ppc64@npm:0.27.2"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-arm64@npm:0.25.1"
@@ -598,6 +605,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/android-arm64@npm:0.27.2"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-arm@npm:0.25.1"
@@ -612,6 +626,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/android-arm@npm:0.27.2"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-x64@npm:0.25.1"
@@ -626,6 +647,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/android-x64@npm:0.27.2"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/darwin-arm64@npm:0.25.1"
@@ -640,6 +668,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/darwin-arm64@npm:0.27.2"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/darwin-x64@npm:0.25.1"
@@ -654,6 +689,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/darwin-x64@npm:0.27.2"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/freebsd-arm64@npm:0.25.1"
@@ -668,6 +710,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/freebsd-arm64@npm:0.27.2"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/freebsd-x64@npm:0.25.1"
@@ -682,6 +731,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/freebsd-x64@npm:0.27.2"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-arm64@npm:0.25.1"
@@ -696,6 +752,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-arm64@npm:0.27.2"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-arm@npm:0.25.1"
@@ -710,6 +773,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-arm@npm:0.27.2"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-ia32@npm:0.25.1"
@@ -724,6 +794,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-ia32@npm:0.27.2"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-loong64@npm:0.25.1"
@@ -738,6 +815,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-loong64@npm:0.27.2"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-mips64el@npm:0.25.1"
@@ -752,6 +836,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-mips64el@npm:0.27.2"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-ppc64@npm:0.25.1"
@@ -766,6 +857,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-ppc64@npm:0.27.2"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-riscv64@npm:0.25.1"
@@ -780,6 +878,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-riscv64@npm:0.27.2"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-s390x@npm:0.25.1"
@@ -794,6 +899,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-s390x@npm:0.27.2"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-x64@npm:0.25.1"
@@ -808,6 +920,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/linux-x64@npm:0.27.2"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/netbsd-arm64@npm:0.25.1"
@@ -822,6 +941,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/netbsd-arm64@npm:0.27.2"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/netbsd-x64@npm:0.25.1"
@@ -836,6 +962,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/netbsd-x64@npm:0.27.2"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/openbsd-arm64@npm:0.25.1"
@@ -850,6 +983,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/openbsd-arm64@npm:0.27.2"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/openbsd-x64@npm:0.25.1"
@@ -864,6 +1004,20 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/openbsd-x64@npm:0.27.2"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openharmony-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/openharmony-arm64@npm:0.27.2"
conditions: os=openharmony & cpu=arm64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/sunos-x64@npm:0.25.1"
@@ -878,6 +1032,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/sunos-x64@npm:0.27.2"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/win32-arm64@npm:0.25.1"
@@ -892,6 +1053,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/win32-arm64@npm:0.27.2"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/win32-ia32@npm:0.25.1"
@@ -906,6 +1074,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/win32-ia32@npm:0.27.2"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:*":
version: 0.25.9
resolution: "@esbuild/win32-x64@npm:0.25.9"
@@ -927,6 +1102,13 @@ __metadata:
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.27.2":
version: 0.27.2
resolution: "@esbuild/win32-x64@npm:0.27.2"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -1528,6 +1710,13 @@ __metadata:
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:^1.5.5":
version: 1.5.5
resolution: "@jridgewell/sourcemap-codec@npm:1.5.5"
checksum: 10/5d9d207b462c11e322d71911e55e21a4e2772f71ffe8d6f1221b8eb5ae6774458c1d242f897fb0814e8714ca9a6b498abfa74dfe4f434493342902b1a48b33a5
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28":
version: 0.3.29
resolution: "@jridgewell/trace-mapping@npm:0.3.29"
@@ -3423,6 +3612,13 @@ __metadata:
languageName: node
linkType: hard
"@standard-schema/spec@npm:^1.0.0":
version: 1.1.0
resolution: "@standard-schema/spec@npm:1.1.0"
checksum: 10/a209615c9e8b2ea535d7db0a5f6aa0f962fd4ab73ee86a46c100fb78116964af1f55a27c1794d4801e534a196794223daa25ff5135021e03c7828aa3d95e1763
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.4.16":
version: 1.4.16
resolution: "@swc/core-darwin-arm64@npm:1.4.16"
@@ -4847,6 +5043,20 @@ __metadata:
languageName: node
linkType: hard
"@vitest/expect@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/expect@npm:4.0.16"
dependencies:
"@standard-schema/spec": "npm:^1.0.0"
"@types/chai": "npm:^5.2.2"
"@vitest/spy": "npm:4.0.16"
"@vitest/utils": "npm:4.0.16"
chai: "npm:^6.2.1"
tinyrainbow: "npm:^3.0.3"
checksum: 10/1da98c86d394a4955bef381ac2c63a52d2eec0086f55e18858083da928cfdf51e7a30bfd88b1814e861906dae44d089aeab0fcc67b2597a4a8073c70cd14bdf7
languageName: node
linkType: hard
"@vitest/mocker@npm:3.0.8":
version: 3.0.8
resolution: "@vitest/mocker@npm:3.0.8"
@@ -4885,6 +5095,25 @@ __metadata:
languageName: node
linkType: hard
"@vitest/mocker@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/mocker@npm:4.0.16"
dependencies:
"@vitest/spy": "npm:4.0.16"
estree-walker: "npm:^3.0.3"
magic-string: "npm:^0.30.21"
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
checksum: 10/3a34c6571ef278b80d33feabb8389d6cf7cfd248fe592b8b2a373650ab460b95805fde65e6bd76aebc75729fc0c94b4d8b9bba25fa55e21c2745ae03c10316bf
languageName: node
linkType: hard
"@vitest/pretty-format@npm:3.0.8":
version: 3.0.8
resolution: "@vitest/pretty-format@npm:3.0.8"
@@ -4903,6 +5132,15 @@ __metadata:
languageName: node
linkType: hard
"@vitest/pretty-format@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/pretty-format@npm:4.0.16"
dependencies:
tinyrainbow: "npm:^3.0.3"
checksum: 10/914d5d35fb3b0aa67f8e6065ac3d1f1798b7774e1ad9d1e873e7c6efdc7925c98e0f8188bb13c4f3feb4d80b756c337f7a55cd4f78c50fe786330d0aaede7cfd
languageName: node
linkType: hard
"@vitest/runner@npm:3.2.4":
version: 3.2.4
resolution: "@vitest/runner@npm:3.2.4"
@@ -4914,6 +5152,16 @@ __metadata:
languageName: node
linkType: hard
"@vitest/runner@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/runner@npm:4.0.16"
dependencies:
"@vitest/utils": "npm:4.0.16"
pathe: "npm:^2.0.3"
checksum: 10/2aed39bb46ba747bd4fd5acf081e9e500192fec19c1887399f6a1701bbfdab05f3d3b45c00e4af5b90a0832853c959a0f64e676b05c67f5457b7c6984f844aa2
languageName: node
linkType: hard
"@vitest/snapshot@npm:3.2.4":
version: 3.2.4
resolution: "@vitest/snapshot@npm:3.2.4"
@@ -4925,6 +5173,17 @@ __metadata:
languageName: node
linkType: hard
"@vitest/snapshot@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/snapshot@npm:4.0.16"
dependencies:
"@vitest/pretty-format": "npm:4.0.16"
magic-string: "npm:^0.30.21"
pathe: "npm:^2.0.3"
checksum: 10/30f2977c96645c018b9d1f658e758f4f886ac63966dca909e9f736d6c9d6d0a6dabdeaedf9abcc13e1000458e4069283632c0140033972847dc1f4b4ac38e076
languageName: node
linkType: hard
"@vitest/spy@npm:3.0.8":
version: 3.0.8
resolution: "@vitest/spy@npm:3.0.8"
@@ -4943,6 +5202,13 @@ __metadata:
languageName: node
linkType: hard
"@vitest/spy@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/spy@npm:4.0.16"
checksum: 10/76cbabfdd77adf16904d5c128de67abca650bbc2ed36acc68fca548dc51844c7fc1ac516e384d07341b25ae39318c7c2feb499ffa7283a1a838f762cb0cda6ab
languageName: node
linkType: hard
"@vitest/utils@npm:3.0.8":
version: 3.0.8
resolution: "@vitest/utils@npm:3.0.8"
@@ -4965,6 +5231,16 @@ __metadata:
languageName: node
linkType: hard
"@vitest/utils@npm:4.0.16":
version: 4.0.16
resolution: "@vitest/utils@npm:4.0.16"
dependencies:
"@vitest/pretty-format": "npm:4.0.16"
tinyrainbow: "npm:^3.0.3"
checksum: 10/07fb3c96867656ff080df7ae6056a8dc23931d0f8bc16e15994c576c580dc6e2dcf71af0964fee197ea7eea4f4ad72c256f56cd3b81599f9e0ba63a228968d50
languageName: node
linkType: hard
"@xenova/transformers@npm:2.17.2, @xenova/transformers@npm:^2.17.2":
version: 2.17.2
resolution: "@xenova/transformers@npm:2.17.2"
@@ -5878,6 +6154,13 @@ __metadata:
languageName: node
linkType: hard
"chai@npm:^6.2.1":
version: 6.2.2
resolution: "chai@npm:6.2.2"
checksum: 10/13cda42cc40aa46da04a41cf7e5c61df6b6ae0b4e8a8c8b40e04d6947e4d7951377ea8c14f9fa7fe5aaa9e8bd9ba414f11288dc958d4cee6f5221b9436f2778f
languageName: node
linkType: hard
"chalk@npm:^3.0.0":
version: 3.0.0
resolution: "chalk@npm:3.0.0"
@@ -6381,9 +6664,9 @@ __metadata:
prettier: "npm:^2.8.3"
tsx: "npm:^4.19.3"
typescript: "npm:^4.9.5 || ^5.4.5"
vitest: "npm:^3.2.4"
vitest: "npm:^4.0.16"
bin:
create-langgraph: dist/cli.mjs
create-langgraph: dist/cli.js
languageName: unknown
linkType: soft
@@ -7654,6 +7937,95 @@ __metadata:
languageName: node
linkType: hard
"esbuild@npm:^0.27.0":
version: 0.27.2
resolution: "esbuild@npm:0.27.2"
dependencies:
"@esbuild/aix-ppc64": "npm:0.27.2"
"@esbuild/android-arm": "npm:0.27.2"
"@esbuild/android-arm64": "npm:0.27.2"
"@esbuild/android-x64": "npm:0.27.2"
"@esbuild/darwin-arm64": "npm:0.27.2"
"@esbuild/darwin-x64": "npm:0.27.2"
"@esbuild/freebsd-arm64": "npm:0.27.2"
"@esbuild/freebsd-x64": "npm:0.27.2"
"@esbuild/linux-arm": "npm:0.27.2"
"@esbuild/linux-arm64": "npm:0.27.2"
"@esbuild/linux-ia32": "npm:0.27.2"
"@esbuild/linux-loong64": "npm:0.27.2"
"@esbuild/linux-mips64el": "npm:0.27.2"
"@esbuild/linux-ppc64": "npm:0.27.2"
"@esbuild/linux-riscv64": "npm:0.27.2"
"@esbuild/linux-s390x": "npm:0.27.2"
"@esbuild/linux-x64": "npm:0.27.2"
"@esbuild/netbsd-arm64": "npm:0.27.2"
"@esbuild/netbsd-x64": "npm:0.27.2"
"@esbuild/openbsd-arm64": "npm:0.27.2"
"@esbuild/openbsd-x64": "npm:0.27.2"
"@esbuild/openharmony-arm64": "npm:0.27.2"
"@esbuild/sunos-x64": "npm:0.27.2"
"@esbuild/win32-arm64": "npm:0.27.2"
"@esbuild/win32-ia32": "npm:0.27.2"
"@esbuild/win32-x64": "npm:0.27.2"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-arm64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/openharmony-arm64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 10/7f1229328b0efc63c4184a61a7eb303df1e99818cc1d9e309fb92600703008e69821e8e984e9e9f54a627da14e0960d561db3a93029482ef96dc82dd267a60c2
languageName: node
linkType: hard
"esbuild@npm:~0.25.0":
version: 0.25.4
resolution: "esbuild@npm:0.25.4"
@@ -8072,6 +8444,13 @@ __metadata:
languageName: node
linkType: hard
"expect-type@npm:^1.2.2":
version: 1.3.0
resolution: "expect-type@npm:1.3.0"
checksum: 10/a5fada3d0c621649261f886e7d93e6bf80ce26d8a86e5d517e38301b8baec8450ab2cb94ba6e7a0a6bf2fc9ee55f54e1b06938ef1efa52ddcfeffbfa01acbbcc
languageName: node
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
@@ -10124,6 +10503,15 @@ __metadata:
languageName: node
linkType: hard
"magic-string@npm:^0.30.21":
version: 0.30.21
resolution: "magic-string@npm:0.30.21"
dependencies:
"@jridgewell/sourcemap-codec": "npm:^1.5.5"
checksum: 10/57d5691f41ed40d962d8bd300148114f53db67fadbff336207db10a99f2bdf4a1be9cac3a68ee85dba575912ee1d4402e4396408196ec2d3afd043b076156221
languageName: node
linkType: hard
"make-fetch-happen@npm:^13.0.0":
version: 13.0.0
resolution: "make-fetch-happen@npm:13.0.0"
@@ -10918,6 +11306,13 @@ __metadata:
languageName: node
linkType: hard
"obug@npm:^2.1.1":
version: 2.1.1
resolution: "obug@npm:2.1.1"
checksum: 10/bdcf9213361786688019345f3452b95a1dc73710e4b403c82a1994b98bad6abc31b26cb72a482128c5fd53ea9daf6fbb7d0e0e7b2b7e9c8be6d779deeccee07f
languageName: node
linkType: hard
"ollama@npm:^0.5.12":
version: 0.5.18
resolution: "ollama@npm:0.5.18"
@@ -13228,6 +13623,13 @@ __metadata:
languageName: node
linkType: hard
"std-env@npm:^3.10.0":
version: 3.10.0
resolution: "std-env@npm:3.10.0"
checksum: 10/19c9cda4f370b1ffae2b8b08c72167d8c3e5cfa972aaf5c6873f85d0ed2faa729407f5abb194dc33380708c00315002febb6f1e1b484736bfcf9361ad366013a
languageName: node
linkType: hard
"std-env@npm:^3.9.0":
version: 3.9.0
resolution: "std-env@npm:3.9.0"
@@ -13664,6 +14066,13 @@ __metadata:
languageName: node
linkType: hard
"tinyexec@npm:^1.0.2":
version: 1.0.2
resolution: "tinyexec@npm:1.0.2"
checksum: 10/cb709ed4240e873d3816e67f851d445f5676e0ae3a52931a60ff571d93d388da09108c8057b62351766133ee05ff3159dd56c3a0fbd39a5933c6639ce8771405
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.14":
version: 0.2.14
resolution: "tinyglobby@npm:0.2.14"
@@ -13698,6 +14107,13 @@ __metadata:
languageName: node
linkType: hard
"tinyrainbow@npm:^3.0.3":
version: 3.0.3
resolution: "tinyrainbow@npm:3.0.3"
checksum: 10/169cc63c15e1378674180f3207c82c05bfa58fc79992e48792e8d97b4b759012f48e95297900ede24a81f0087cf329a0d85bb81109739eacf03c650127b3f6c1
languageName: node
linkType: hard
"tinyspy@npm:^3.0.2":
version: 3.0.2
resolution: "tinyspy@npm:3.0.2"
@@ -14676,6 +15092,61 @@ __metadata:
languageName: node
linkType: hard
"vite@npm:^6.0.0 || ^7.0.0":
version: 7.3.0
resolution: "vite@npm:7.3.0"
dependencies:
esbuild: "npm:^0.27.0"
fdir: "npm:^6.5.0"
fsevents: "npm:~2.3.3"
picomatch: "npm:^4.0.3"
postcss: "npm:^8.5.6"
rollup: "npm:^4.43.0"
tinyglobby: "npm:^0.2.15"
peerDependencies:
"@types/node": ^20.19.0 || >=22.12.0
jiti: ">=1.21.0"
less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: ">=0.54.8"
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
dependenciesMeta:
fsevents:
optional: true
peerDependenciesMeta:
"@types/node":
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
bin:
vite: bin/vite.js
checksum: 10/044490133aaf4cc024700995edaac10ff182262c721a44a8ac7839207b3e5af435c8b9dd8ee4658dc0f47147fa4211632cca3120177aa4bab99a7cb095e5149e
languageName: node
linkType: hard
"vitest@npm:^3.2.4":
version: 3.2.4
resolution: "vitest@npm:3.2.4"
@@ -14732,6 +15203,65 @@ __metadata:
languageName: node
linkType: hard
"vitest@npm:^4.0.16":
version: 4.0.16
resolution: "vitest@npm:4.0.16"
dependencies:
"@vitest/expect": "npm:4.0.16"
"@vitest/mocker": "npm:4.0.16"
"@vitest/pretty-format": "npm:4.0.16"
"@vitest/runner": "npm:4.0.16"
"@vitest/snapshot": "npm:4.0.16"
"@vitest/spy": "npm:4.0.16"
"@vitest/utils": "npm:4.0.16"
es-module-lexer: "npm:^1.7.0"
expect-type: "npm:^1.2.2"
magic-string: "npm:^0.30.21"
obug: "npm:^2.1.1"
pathe: "npm:^2.0.3"
picomatch: "npm:^4.0.3"
std-env: "npm:^3.10.0"
tinybench: "npm:^2.9.0"
tinyexec: "npm:^1.0.2"
tinyglobby: "npm:^0.2.15"
tinyrainbow: "npm:^3.0.3"
vite: "npm:^6.0.0 || ^7.0.0"
why-is-node-running: "npm:^2.3.0"
peerDependencies:
"@edge-runtime/vm": "*"
"@opentelemetry/api": ^1.9.0
"@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0
"@vitest/browser-playwright": 4.0.16
"@vitest/browser-preview": 4.0.16
"@vitest/browser-webdriverio": 4.0.16
"@vitest/ui": 4.0.16
happy-dom: "*"
jsdom: "*"
peerDependenciesMeta:
"@edge-runtime/vm":
optional: true
"@opentelemetry/api":
optional: true
"@types/node":
optional: true
"@vitest/browser-playwright":
optional: true
"@vitest/browser-preview":
optional: true
"@vitest/browser-webdriverio":
optional: true
"@vitest/ui":
optional: true
happy-dom:
optional: true
jsdom:
optional: true
bin:
vitest: vitest.mjs
checksum: 10/22b3806988ab186be4a6a133903a70c62835198e8e749f6ed751957d23bc1e3f0466e310a1a79d0b70a354b2e308e574486191eb39711257b3fe61e4fe00d1c8
languageName: node
linkType: hard
"vm-browserify@npm:^1.0.1":
version: 1.1.2
resolution: "vm-browserify@npm:1.1.2"