npm cli app
This commit is contained in:
parent
b62a25c5eb
commit
aed85dff72
|
|
@ -0,0 +1,52 @@
|
|||
# Source files (only compiled JS will be included)
|
||||
*.ts
|
||||
!*.d.ts
|
||||
|
||||
# Tests
|
||||
tests/
|
||||
**/*.test.ts
|
||||
**/*.test.js
|
||||
|
||||
# Build config
|
||||
tsconfig.json
|
||||
tsconfig.build.json
|
||||
bun.lockb
|
||||
|
||||
# Development
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
anvil.log
|
||||
.anvil.pid
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
|
||||
# Documentation (keep README.md)
|
||||
docs/
|
||||
|
||||
# Deployments
|
||||
deployments/
|
||||
|
||||
# Node modules (will be installed by user)
|
||||
node_modules/
|
||||
|
||||
# Parent directory references
|
||||
../
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
# Installing NLA CLI Globally
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js >= 18.0.0
|
||||
- npm or yarn or pnpm
|
||||
- A blockchain node (local Anvil or remote RPC URL)
|
||||
- At least one LLM provider API key (OpenAI, Anthropic, or OpenRouter)
|
||||
|
||||
## Installation
|
||||
|
||||
### Global Installation (Recommended)
|
||||
|
||||
Install the NLA CLI globally to use it from anywhere:
|
||||
|
||||
```bash
|
||||
npm install -g nla
|
||||
```
|
||||
|
||||
Or with yarn:
|
||||
```bash
|
||||
yarn global add nla
|
||||
```
|
||||
|
||||
Or with pnpm:
|
||||
```bash
|
||||
pnpm add -g nla
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
nla --help
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a `.env` file in your project directory:
|
||||
|
||||
```bash
|
||||
# Copy the example environment file
|
||||
cp node_modules/nla/.env.example .env
|
||||
|
||||
# Edit with your configuration
|
||||
nano .env
|
||||
```
|
||||
|
||||
Required environment variables:
|
||||
```bash
|
||||
# At least one LLM provider API key
|
||||
OPENAI_API_KEY=sk-...
|
||||
# OR
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
# OR
|
||||
OPENROUTER_API_KEY=sk-or-...
|
||||
|
||||
# Oracle configuration
|
||||
ORACLE_PRIVATE_KEY=0x...
|
||||
RPC_URL=http://localhost:8545
|
||||
|
||||
# Optional: For enhanced search
|
||||
PERPLEXITY_API_KEY=pplx-...
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Start Development Environment
|
||||
|
||||
```bash
|
||||
nla dev
|
||||
```
|
||||
|
||||
This will:
|
||||
- Start Anvil (local blockchain)
|
||||
- Deploy all contracts
|
||||
- Start the oracle
|
||||
|
||||
### 2. Create an Escrow
|
||||
|
||||
```bash
|
||||
nla escrow:create \
|
||||
--demand "The sky is blue" \
|
||||
--amount 10 \
|
||||
--token 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 \
|
||||
--oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
|
||||
```
|
||||
|
||||
### 3. Fulfill an Escrow
|
||||
|
||||
```bash
|
||||
nla escrow:fulfill \
|
||||
--escrow-uid 0x... \
|
||||
--fulfillment "The sky appears blue today" \
|
||||
--oracle 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
|
||||
```
|
||||
|
||||
### 4. Collect Payment
|
||||
|
||||
```bash
|
||||
nla escrow:collect \
|
||||
--escrow-uid 0x... \
|
||||
--fulfillment-uid 0x...
|
||||
```
|
||||
|
||||
## Uninstallation
|
||||
|
||||
```bash
|
||||
npm uninstall -g nla
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
If you want to contribute or modify the CLI:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/arkhai-io/natural-language-agreements.git
|
||||
cd natural-language-agreements
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build
|
||||
npm run build
|
||||
|
||||
# Link locally
|
||||
npm link
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Command not found after installation
|
||||
|
||||
Make sure your npm global bin directory is in your PATH:
|
||||
|
||||
```bash
|
||||
# Check npm global bin path
|
||||
npm bin -g
|
||||
|
||||
# Add to PATH (add to ~/.bashrc or ~/.zshrc)
|
||||
export PATH="$(npm bin -g):$PATH"
|
||||
```
|
||||
|
||||
### Permission errors on Linux/Mac
|
||||
|
||||
If you get permission errors, either:
|
||||
|
||||
1. Use a Node version manager (recommended):
|
||||
```bash
|
||||
# Install nvm
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
|
||||
|
||||
# Install and use Node
|
||||
nvm install 18
|
||||
nvm use 18
|
||||
|
||||
# Now install without sudo
|
||||
npm install -g nla
|
||||
```
|
||||
|
||||
2. Or configure npm to use a different directory:
|
||||
```bash
|
||||
mkdir ~/.npm-global
|
||||
npm config set prefix '~/.npm-global'
|
||||
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- GitHub: https://github.com/arkhai-io/natural-language-agreements/issues
|
||||
- Documentation: https://github.com/arkhai-io/natural-language-agreements
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
import { spawn, spawnSync } from "child_process";
|
||||
import { existsSync, readFileSync, writeFileSync, createWriteStream, unlinkSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
// Colors for console output
|
||||
const colors = {
|
||||
green: '\x1b[32m',
|
||||
blue: '\x1b[34m',
|
||||
red: '\x1b[31m',
|
||||
yellow: '\x1b[33m',
|
||||
reset: '\x1b[0m'
|
||||
};
|
||||
|
||||
// Load .env file if it exists
|
||||
function loadEnvFile() {
|
||||
const envPath = '.env';
|
||||
if (existsSync(envPath)) {
|
||||
console.log(`${colors.blue}📄 Loading .env file...${colors.reset}`);
|
||||
const envContent = readFileSync(envPath, 'utf-8');
|
||||
const lines = envContent.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
// Skip comments and empty lines
|
||||
if (line.trim().startsWith('#') || !line.trim()) continue;
|
||||
|
||||
// Parse key=value
|
||||
const match = line.match(/^([^=]+)=(.*)$/);
|
||||
if (match) {
|
||||
const key = match[1].trim();
|
||||
const value = match[2].trim();
|
||||
// Only set if not already in environment
|
||||
if (!process.env[key]) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`${colors.green}✅ Environment variables loaded${colors.reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check if a command exists
|
||||
function commandExists(command: string): boolean {
|
||||
try {
|
||||
const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [command], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
return result.status === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check if a port is in use
|
||||
function isPortInUse(port: number): boolean {
|
||||
try {
|
||||
const result = spawnSync('lsof', ['-Pi', `:${port}`, '-sTCP:LISTEN', '-t'], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
return result.status === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Dev command - Start complete development environment
|
||||
export async function runDevCommand(cliDir: string) {
|
||||
console.log(`${colors.blue}════════════════════════════════════════════════════════${colors.reset}`);
|
||||
console.log(`${colors.blue} Natural Language Agreement Oracle - Quick Setup${colors.reset}`);
|
||||
console.log(`${colors.blue}════════════════════════════════════════════════════════${colors.reset}\n`);
|
||||
|
||||
// Load .env file first
|
||||
loadEnvFile();
|
||||
console.log('');
|
||||
|
||||
// Check prerequisites
|
||||
console.log(`${colors.blue}📋 Checking prerequisites...${colors.reset}\n`);
|
||||
|
||||
// Check Bun
|
||||
if (!commandExists('bun')) {
|
||||
console.error(`${colors.red}❌ Bun is not installed${colors.reset}`);
|
||||
console.log('Please install it: https://bun.sh');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${colors.green}✅ Bun installed${colors.reset}`);
|
||||
|
||||
// Check Foundry
|
||||
if (!commandExists('forge')) {
|
||||
console.error(`${colors.red}❌ Foundry (forge) is not installed${colors.reset}`);
|
||||
console.log('Please install it: https://book.getfoundry.sh/getting-started/installation');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${colors.green}✅ Foundry installed${colors.reset}`);
|
||||
|
||||
// Check Anvil
|
||||
if (!commandExists('anvil')) {
|
||||
console.error(`${colors.red}❌ Anvil is not installed${colors.reset}`);
|
||||
console.log('Please install Foundry: https://book.getfoundry.sh/getting-started/installation');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${colors.green}✅ Anvil installed${colors.reset}`);
|
||||
|
||||
// Check LLM API keys
|
||||
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
||||
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
||||
const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
|
||||
const hasPerplexity = !!process.env.PERPLEXITY_API_KEY;
|
||||
|
||||
if (!hasOpenAI && !hasAnthropic && !hasOpenRouter && !hasPerplexity) {
|
||||
console.error(`${colors.red}❌ No LLM provider API key set${colors.reset}`);
|
||||
console.log('Please add at least one API key to your environment:');
|
||||
console.log(' export OPENAI_API_KEY=sk-...');
|
||||
console.log(' export ANTHROPIC_API_KEY=sk-ant-...');
|
||||
console.log(' export OPENROUTER_API_KEY=sk-or-...');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (hasOpenAI) console.log(`${colors.green}✅ OpenAI API key configured${colors.reset}`);
|
||||
if (hasAnthropic) console.log(`${colors.green}✅ Anthropic API key configured${colors.reset}`);
|
||||
if (hasOpenRouter) console.log(`${colors.green}✅ OpenRouter API key configured${colors.reset}`);
|
||||
if (hasPerplexity) console.log(`${colors.green}✅ Perplexity API key configured${colors.reset}`);
|
||||
console.log('');
|
||||
|
||||
// Check if Anvil is already running
|
||||
if (isPortInUse(8545)) {
|
||||
console.log(`${colors.yellow}⚠️ Anvil is already running on port 8545${colors.reset}`);
|
||||
console.log(`${colors.blue}Using existing Anvil instance${colors.reset}\n`);
|
||||
} else {
|
||||
// Start Anvil
|
||||
console.log(`${colors.blue}🔨 Starting Anvil...${colors.reset}`);
|
||||
const anvilProcess = spawn('anvil', [], {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
detached: true
|
||||
});
|
||||
|
||||
// Save PID
|
||||
writeFileSync('.anvil.pid', anvilProcess.pid!.toString());
|
||||
|
||||
// Redirect output to log file
|
||||
const logStream = createWriteStream('anvil.log', { flags: 'a' });
|
||||
anvilProcess.stdout?.pipe(logStream);
|
||||
anvilProcess.stderr?.pipe(logStream);
|
||||
|
||||
anvilProcess.unref();
|
||||
|
||||
console.log(`${colors.green}✅ Anvil started (PID: ${anvilProcess.pid})${colors.reset}`);
|
||||
console.log(' Logs: tail -f anvil.log');
|
||||
|
||||
// Wait for Anvil to be ready
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
}
|
||||
|
||||
// Deploy contracts
|
||||
console.log(`\n${colors.blue}📝 Deploying contracts...${colors.reset}\n`);
|
||||
const deployScript = join(cliDir, 'server', 'deploy.js');
|
||||
const deployResult = spawnSync('bun', ['run', deployScript, '--network', 'localhost', '--rpc-url', 'http://localhost:8545'], {
|
||||
stdio: 'inherit',
|
||||
cwd: process.cwd()
|
||||
});
|
||||
|
||||
if (deployResult.status !== 0) {
|
||||
console.error(`${colors.red}❌ Deployment failed${colors.reset}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Start oracle
|
||||
console.log(`\n${colors.blue}🚀 Starting oracle...${colors.reset}\n`);
|
||||
const oracleScript = join(cliDir, 'server', 'oracle.js');
|
||||
const deploymentFile = join(cliDir, 'deployments', 'localhost.json');
|
||||
|
||||
const oracleProcess = spawn('bun', ['run', oracleScript, '--deployment', deploymentFile], {
|
||||
stdio: 'inherit',
|
||||
cwd: process.cwd()
|
||||
});
|
||||
|
||||
// Handle cleanup on exit
|
||||
const cleanup = () => {
|
||||
console.log(`\n${colors.yellow}🛑 Shutting down...${colors.reset}`);
|
||||
|
||||
// Kill oracle
|
||||
oracleProcess.kill();
|
||||
|
||||
// Kill Anvil
|
||||
try {
|
||||
const pidFile = '.anvil.pid';
|
||||
if (existsSync(pidFile)) {
|
||||
const pid = readFileSync(pidFile, 'utf-8').trim();
|
||||
try {
|
||||
process.kill(parseInt(pid));
|
||||
console.log(`${colors.green}✅ Anvil stopped${colors.reset}`);
|
||||
} catch (e) {
|
||||
// Process might already be dead
|
||||
}
|
||||
unlinkSync(pidFile);
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on('SIGINT', cleanup);
|
||||
process.on('SIGTERM', cleanup);
|
||||
|
||||
// Keep process alive
|
||||
await new Promise(() => {});
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { spawnSync } from "child_process";
|
||||
import { existsSync, readFileSync, unlinkSync } from "fs";
|
||||
|
||||
// Colors for console output
|
||||
const colors = {
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
red: '\x1b[31m',
|
||||
reset: '\x1b[0m'
|
||||
};
|
||||
|
||||
// Stop command - Stop all services
|
||||
export async function runStopCommand() {
|
||||
console.log(`${colors.yellow}🛑 Stopping services...${colors.reset}\n`);
|
||||
|
||||
// Stop Anvil
|
||||
try {
|
||||
const pidFile = '.anvil.pid';
|
||||
if (existsSync(pidFile)) {
|
||||
const pid = readFileSync(pidFile, 'utf-8').trim();
|
||||
try {
|
||||
process.kill(parseInt(pid));
|
||||
console.log(`${colors.green}✅ Anvil stopped${colors.reset}`);
|
||||
} catch (e) {
|
||||
console.log(`${colors.yellow}⚠️ Anvil process not found (may have already stopped)${colors.reset}`);
|
||||
}
|
||||
unlinkSync(pidFile);
|
||||
} else {
|
||||
console.log(`${colors.yellow}⚠️ No Anvil PID file found${colors.reset}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`${colors.red}❌ Error stopping Anvil:${colors.reset}`, e);
|
||||
}
|
||||
|
||||
// Try to kill any remaining processes on port 8545
|
||||
try {
|
||||
spawnSync('pkill', ['-f', 'anvil']);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
console.log(`\n${colors.green}✅ Services stopped${colors.reset}`);
|
||||
}
|
||||
54
cli/index.ts
54
cli/index.ts
|
|
@ -1,11 +1,20 @@
|
|||
#!/usr/bin/env bun
|
||||
#!/usr/bin/env node
|
||||
import { parseArgs } from "util";
|
||||
import { spawnSync } from "child_process";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
import { createPublicClient, http, parseAbiParameters, decodeAbiParameters } from "viem";
|
||||
import { foundry } from "viem/chains";
|
||||
import { contracts } from "alkahest-ts";
|
||||
|
||||
import { runDevCommand } from "./commands/dev.js";
|
||||
import { runStopCommand } from "./commands/stop.js";
|
||||
|
||||
// Get the directory name for ESM modules (compatible with both Node and Bun)
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
// Helper function to display usage
|
||||
function displayHelp() {
|
||||
console.log(`
|
||||
|
|
@ -83,7 +92,7 @@ Examples:
|
|||
|
||||
// Parse command line arguments
|
||||
function parseCliArgs() {
|
||||
const args = Bun.argv.slice(2);
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
displayHelp();
|
||||
|
|
@ -120,27 +129,9 @@ function parseCliArgs() {
|
|||
return { command, ...values };
|
||||
}
|
||||
|
||||
// Shell command handler
|
||||
async function runShellCommand(scriptName: string, args: string[] = []) {
|
||||
const { spawnSync } = await import("child_process");
|
||||
const scriptDir = import.meta.dir;
|
||||
const scriptPath = `${scriptDir}/scripts/${scriptName}`;
|
||||
|
||||
// Run the shell script
|
||||
const result = spawnSync(scriptPath, args, {
|
||||
stdio: "inherit",
|
||||
cwd: process.cwd(),
|
||||
shell: true,
|
||||
});
|
||||
|
||||
process.exit(result.status || 0);
|
||||
}
|
||||
|
||||
// Server command handler (for deploy.ts, oracle.ts)
|
||||
async function runServerCommand(scriptName: string, args: string[] = []) {
|
||||
const { spawnSync } = await import("child_process");
|
||||
const scriptDir = import.meta.dir;
|
||||
const scriptPath = `${scriptDir}/server/${scriptName}`;
|
||||
const scriptPath = join(__dirname, "server", scriptName);
|
||||
|
||||
// Run the TypeScript file directly
|
||||
const result = spawnSync("bun", ["run", scriptPath, ...args], {
|
||||
|
|
@ -157,25 +148,25 @@ async function main() {
|
|||
const args = parseCliArgs();
|
||||
const command = args.command;
|
||||
|
||||
// Handle shell script commands (dev and stop need shell for process management)
|
||||
// Handle dev and stop commands
|
||||
if (command === "dev") {
|
||||
await runShellCommand("dev.sh");
|
||||
await runDevCommand(__dirname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "stop") {
|
||||
await runShellCommand("stop.sh");
|
||||
await runStopCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle TypeScript commands that can run directly
|
||||
if (command === "deploy") {
|
||||
await runServerCommand("deploy.ts", Bun.argv.slice(3));
|
||||
await runServerCommand("deploy.js", process.argv.slice(3));
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "start-oracle") {
|
||||
await runServerCommand("oracle.ts", Bun.argv.slice(3));
|
||||
await runServerCommand("oracle.js", process.argv.slice(3));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -184,13 +175,13 @@ async function main() {
|
|||
|
||||
switch (command) {
|
||||
case "escrow:create":
|
||||
scriptPath = "./client/create-escrow.ts";
|
||||
scriptPath = "./client/create-escrow.js";
|
||||
break;
|
||||
case "escrow:fulfill":
|
||||
scriptPath = "./client/fulfill-escrow.ts";
|
||||
scriptPath = "./client/fulfill-escrow.js";
|
||||
break;
|
||||
case "escrow:collect":
|
||||
scriptPath = "./client/collect-escrow.ts";
|
||||
scriptPath = "./client/collect-escrow.js";
|
||||
break;
|
||||
case "escrow:status":
|
||||
await runStatusCommand(args);
|
||||
|
|
@ -203,11 +194,10 @@ async function main() {
|
|||
|
||||
// Run the command as a subprocess with the args (excluding the command name)
|
||||
const { spawnSync } = await import("child_process");
|
||||
const scriptDir = import.meta.dir;
|
||||
const fullScriptPath = `${scriptDir}/${scriptPath}`;
|
||||
const fullScriptPath = join(__dirname, scriptPath);
|
||||
|
||||
// Build args array without the command name
|
||||
const commandArgs = Bun.argv.slice(3); // Skip bun, script, and command
|
||||
const commandArgs = process.argv.slice(3); // Skip node, script, and command
|
||||
|
||||
const result = spawnSync("bun", ["run", fullScriptPath, ...commandArgs], {
|
||||
stdio: "inherit",
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Complete setup and deployment for local development
|
||||
# This script will:
|
||||
# 1. Check prerequisites
|
||||
# 2. Start Anvil
|
||||
# 3. Deploy contracts
|
||||
# 4. Start the oracle
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} Natural Language Agreement Oracle - Quick Setup${NC}"
|
||||
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
# Check prerequisites
|
||||
echo -e "${BLUE}📋 Checking prerequisites...${NC}\n"
|
||||
|
||||
# Load .env file if it exists
|
||||
if [ -f ".env" ]; then
|
||||
echo -e "${BLUE}📄 Loading .env file...${NC}"
|
||||
export $(cat .env | grep -v '^#' | xargs)
|
||||
echo -e "${GREEN}✅ Environment variables loaded${NC}"
|
||||
fi
|
||||
|
||||
# Check Bun
|
||||
if ! command -v bun &> /dev/null; then
|
||||
echo -e "${RED}❌ Bun is not installed${NC}"
|
||||
echo "Please install it: https://bun.sh"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✅ Bun installed${NC}"
|
||||
|
||||
# Check Foundry
|
||||
if ! command -v forge &> /dev/null; then
|
||||
echo -e "${RED}❌ Foundry (forge) is not installed${NC}"
|
||||
echo "Please install it: https://book.getfoundry.sh/getting-started/installation"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✅ Foundry installed${NC}"
|
||||
|
||||
# Check Anvil
|
||||
if ! command -v anvil &> /dev/null; then
|
||||
echo -e "${RED}❌ Anvil is not installed${NC}"
|
||||
echo "Please install Foundry: https://book.getfoundry.sh/getting-started/installation"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✅ Anvil installed${NC}"
|
||||
|
||||
# Check alkahest
|
||||
if [ ! -d "../alkahest" ]; then
|
||||
echo -e "${RED}❌ alkahest repository not found${NC}"
|
||||
echo "Please clone it in the parent directory:"
|
||||
echo " cd .. && git clone https://github.com/arkhai-io/alkahest.git"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✅ alkahest repository found${NC}"
|
||||
|
||||
# Check LLM API keys
|
||||
if [ -z "$OPENAI_API_KEY" ] && [ -z "$ANTHROPIC_API_KEY" ] && [ -z "$OPENROUTER_API_KEY" ]; then
|
||||
echo -e "${RED}❌ No LLM provider API key set${NC}"
|
||||
echo "Please add at least one API key to your .env file:"
|
||||
echo " OPENAI_API_KEY=sk-..."
|
||||
echo " ANTHROPIC_API_KEY=sk-ant-..."
|
||||
echo " OPENROUTER_API_KEY=sk-or-..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$OPENAI_API_KEY" ]; then
|
||||
echo -e "${GREEN}✅ OpenAI API key configured${NC}"
|
||||
fi
|
||||
if [ -n "$ANTHROPIC_API_KEY" ]; then
|
||||
echo -e "${GREEN}✅ Anthropic API key configured${NC}"
|
||||
fi
|
||||
if [ -n "$OPENROUTER_API_KEY" ]; then
|
||||
echo -e "${GREEN}✅ OpenRouter API key configured${NC}"
|
||||
fi
|
||||
if [ -n "$PERPLEXITY_API_KEY" ]; then
|
||||
echo -e "${GREEN}✅ Perplexity API key configured${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Install dependencies
|
||||
echo -e "${BLUE}📦 Installing dependencies...${NC}\n"
|
||||
bun install
|
||||
|
||||
# Check if Anvil is already running
|
||||
if lsof -Pi :8545 -sTCP:LISTEN -t >/dev/null ; then
|
||||
echo -e "${YELLOW}⚠️ Anvil is already running on port 8545${NC}"
|
||||
read -p "Do you want to kill it and start fresh? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
pkill -f anvil || true
|
||||
sleep 2
|
||||
else
|
||||
echo -e "${BLUE}Using existing Anvil instance${NC}\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start Anvil in background if not running
|
||||
if ! lsof -Pi :8545 -sTCP:LISTEN -t >/dev/null ; then
|
||||
echo -e "${BLUE}🔨 Starting Anvil...${NC}"
|
||||
anvil > anvil.log 2>&1 &
|
||||
ANVIL_PID=$!
|
||||
echo $ANVIL_PID > .anvil.pid
|
||||
echo -e "${GREEN}✅ Anvil started (PID: $ANVIL_PID)${NC}"
|
||||
echo " Logs: tail -f anvil.log"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Deploy contracts
|
||||
echo -e "\n${BLUE}📝 Deploying contracts...${NC}\n"
|
||||
bun run cli/server/deploy.ts --network localhost --rpc-url http://localhost:8545
|
||||
|
||||
# Start oracle
|
||||
echo -e "\n${BLUE}🚀 Starting oracle...${NC}\n"
|
||||
bun run cli/server/oracle.ts --deployment ./cli/deployments/localhost.json
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo -e "\n${YELLOW}🛑 Shutting down...${NC}"
|
||||
if [ -f .anvil.pid ]; then
|
||||
ANVIL_PID=$(cat .anvil.pid)
|
||||
kill $ANVIL_PID 2>/dev/null || true
|
||||
rm .anvil.pid
|
||||
echo -e "${GREEN}✅ Anvil stopped${NC}"
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Stop all running oracle and Anvil processes
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}🛑 Stopping Natural Language Agreement services...${NC}\n"
|
||||
|
||||
# Stop Anvil
|
||||
if [ -f .anvil.pid ]; then
|
||||
ANVIL_PID=$(cat .anvil.pid)
|
||||
if kill -0 $ANVIL_PID 2>/dev/null; then
|
||||
kill $ANVIL_PID
|
||||
echo -e "${GREEN}✅ Stopped Anvil (PID: $ANVIL_PID)${NC}"
|
||||
fi
|
||||
rm .anvil.pid
|
||||
else
|
||||
# Try to kill any running anvil
|
||||
pkill -f anvil && echo -e "${GREEN}✅ Stopped Anvil${NC}" || echo -e "${YELLOW}⚠️ No Anvil process found${NC}"
|
||||
fi
|
||||
|
||||
# Stop oracle
|
||||
pkill -f "bun run oracle" && echo -e "${GREEN}✅ Stopped oracle${NC}" || echo -e "${YELLOW}⚠️ No oracle process found${NC}"
|
||||
|
||||
echo -e "\n${GREEN}✨ Cleanup complete${NC}"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bun
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Deployment script for Alkahest Natural Language Agreement Oracle
|
||||
*
|
||||
|
|
@ -54,7 +54,7 @@ Examples:
|
|||
// Parse command line arguments
|
||||
function parseCliArgs() {
|
||||
const { values } = parseArgs({
|
||||
args: Bun.argv.slice(2),
|
||||
args: process.argv.slice(2),
|
||||
options: {
|
||||
"network": { type: "string" },
|
||||
"rpc-url": { type: "string" },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bun
|
||||
#!/usr/bin/env node
|
||||
import { parseArgs } from "util";
|
||||
import { parseAbiParameters, createWalletClient, http, publicActions } from "viem";
|
||||
import { privateKeyToAccount } from "viem/accounts";
|
||||
|
|
@ -57,7 +57,7 @@ Examples:
|
|||
// Parse command line arguments
|
||||
function parseCliArgs() {
|
||||
const { values } = parseArgs({
|
||||
args: Bun.argv.slice(2),
|
||||
args: process.argv.slice(2),
|
||||
options: {
|
||||
"rpc-url": { type: "string" },
|
||||
"private-key": { type: "string" },
|
||||
|
|
|
|||
42
package.json
42
package.json
|
|
@ -1,15 +1,45 @@
|
|||
{
|
||||
"name": "natural-language-agreement-extension",
|
||||
"module": "index.ts",
|
||||
"name": "nla",
|
||||
"version": "1.0.0",
|
||||
"description": "Natural Language Agreement Oracle - CLI for creating and managing blockchain agreements using natural language",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
"private": false,
|
||||
"author": "Arkhai",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/arkhai-io/natural-language-agreements.git"
|
||||
},
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"ethereum",
|
||||
"smart-contracts",
|
||||
"natural-language",
|
||||
"oracle",
|
||||
"ai",
|
||||
"llm",
|
||||
"escrow"
|
||||
],
|
||||
"bin": {
|
||||
"nla": "cli/index.ts"
|
||||
"nla": "./dist/cli/index.js"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"README.md",
|
||||
".env.example"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"prepublishOnly": "npm run build",
|
||||
"dev": "bun run index.ts",
|
||||
"start": "bun run index.ts",
|
||||
"test": "bun test ./tests --exclude alkahest-ts/** ",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./",
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["cli/**/*"],
|
||||
"exclude": ["node_modules", "tests", "dist"]
|
||||
}
|
||||
|
|
@ -1,30 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
|
||||
}
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"cli/**/*"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue