@marshalz/git-mantle-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLISH_INSTRUCTIONS.md +57 -0
- package/README.md +178 -0
- package/index.js +444 -0
- package/package.json +43 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Publishing MantlePush CLI to npm
|
|
2
|
+
|
|
3
|
+
## Publishing Steps
|
|
4
|
+
|
|
5
|
+
1. **Verify npm authentication:**
|
|
6
|
+
```bash
|
|
7
|
+
npm whoami
|
|
8
|
+
```
|
|
9
|
+
If not logged in:
|
|
10
|
+
```bash
|
|
11
|
+
npm login
|
|
12
|
+
# Or create account: npm adduser
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. **Check package name availability:**
|
|
16
|
+
```bash
|
|
17
|
+
npm view @marshalz/git-mantle-agent
|
|
18
|
+
```
|
|
19
|
+
Should return 404 (not found) - package name is available!
|
|
20
|
+
|
|
21
|
+
3. **Prepare for publishing:**
|
|
22
|
+
```bash
|
|
23
|
+
cd git-agent-cli
|
|
24
|
+
npm version patch # Increment version (e.g., 1.0.2 ā 1.0.3)
|
|
25
|
+
npm publish
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
4. **Post-publishing installation:**
|
|
29
|
+
Users can now install the MantlePush CLI globally:
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g @marshalz/git-mantle-agent
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Package Details
|
|
35
|
+
|
|
36
|
+
- **Package name**: `@marshalz/git-mantle-agent` (npm registry identifier)
|
|
37
|
+
- **Command name**: `git-mantle-agent` (accessed via Git alias)
|
|
38
|
+
- **Versioning**: Follow semantic versioning (major.minor.patch)
|
|
39
|
+
- **Dependencies**: All required packages must be listed in `package.json`
|
|
40
|
+
|
|
41
|
+
## User Setup Instructions
|
|
42
|
+
|
|
43
|
+
After installing from npm, users configure the Git alias:
|
|
44
|
+
```bash
|
|
45
|
+
git config --global alias.mantle-agent '!git-mantle-agent'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Alternatively, users can invoke the CLI directly:
|
|
49
|
+
```bash
|
|
50
|
+
git-mantle-agent stats
|
|
51
|
+
git-mantle-agent compare main aggressive
|
|
52
|
+
git-mantle-agent logs
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Mantle Network Integration
|
|
56
|
+
|
|
57
|
+
This CLI package is designed specifically for Mantle Sepolia testnet deployments. All agent contracts are deployed to Mantle's Layer 2 infrastructure, providing fast and cost-effective AI agent execution.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# MantlePush CLI
|
|
2
|
+
|
|
3
|
+
**Deploy AI Agents on Mantle with Git** - Professional command-line interface for the MantlePush deployment platform.
|
|
4
|
+
|
|
5
|
+
MantlePush transforms AI agent deployment into a seamless Git-native workflow. Deploy autonomous agents to Mantle Sepolia testnet with a single `git push`. Each branch becomes a unique smart contract, enabling true parallel A/B testing of trading strategies.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### Quick Install (For Users)
|
|
10
|
+
|
|
11
|
+
**Option 1: Install from npm (Recommended)**
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g @marshalz/git-mantle-agent
|
|
14
|
+
|
|
15
|
+
# Configure Git alias for seamless integration
|
|
16
|
+
git config --global alias.mantle-agent '!git-mantle-agent'
|
|
17
|
+
|
|
18
|
+
# Start using MantlePush CLI
|
|
19
|
+
git mantle-agent stats
|
|
20
|
+
git mantle-agent compare main aggressive
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Option 2: Install from source (Development)**
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/xaviersharwin10/somnia-git-agent.git
|
|
26
|
+
cd somnia-git-agent/git-agent-cli
|
|
27
|
+
npm install -g .
|
|
28
|
+
|
|
29
|
+
# Set up Git alias
|
|
30
|
+
git config --global alias.mantle-agent '!git-mantle-agent'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Development
|
|
34
|
+
```bash
|
|
35
|
+
npm link
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## CLI Commands
|
|
39
|
+
|
|
40
|
+
* `git mantle-agent init` - Configure MantlePush deployment pipeline for your repository
|
|
41
|
+
* `git mantle-agent secrets set <KEY=VALUE>` - Store encrypted secrets for the current branch (e.g., `GROQ_API_KEY=sk-...`)
|
|
42
|
+
* `git mantle-agent secrets check` - Verify which secrets are configured for your agent
|
|
43
|
+
* `git mantle-agent stats` - Display real-time performance metrics from your Mantle agent
|
|
44
|
+
* `git mantle-agent logs` - Stream the latest 50 log entries from your agent process
|
|
45
|
+
* `git mantle-agent compare <branch1> <branch2>` - Side-by-side comparison of two agent strategies
|
|
46
|
+
|
|
47
|
+
**Note**: After installing, you need to set up the Git alias once:
|
|
48
|
+
```bash
|
|
49
|
+
git config --global alias.mantle-agent '!git-mantle-agent'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then you can use `git mantle-agent` as shown above.
|
|
53
|
+
|
|
54
|
+
## Key Features
|
|
55
|
+
|
|
56
|
+
- š **One-Command Setup**: Interactive initialization that configures everything automatically
|
|
57
|
+
- š **Enterprise-Grade Security**: AES-256 encrypted secret storage, one set per Git branch
|
|
58
|
+
- š **Live Performance Tracking**: Real-time metrics from agents executing on Mantle Sepolia
|
|
59
|
+
- š **Parallel Strategy Testing**: Compare multiple trading strategies running simultaneously
|
|
60
|
+
- āļø **Native Mantle Integration**: Built specifically for Mantle's Layer 2 infrastructure
|
|
61
|
+
- šØ **Developer-Friendly UX**: Color-coded terminal output with clear, actionable feedback
|
|
62
|
+
- ā” **Lightweight & Fast**: Minimal dependencies, instant command execution
|
|
63
|
+
|
|
64
|
+
## Mantle Sepolia Integration
|
|
65
|
+
|
|
66
|
+
MantlePush leverages Mantle's high-performance Layer 2 infrastructure to deploy and manage AI agents:
|
|
67
|
+
|
|
68
|
+
- **Smart Contract Deployment**: Each Git branch deploys as a unique `Agent.sol` contract on Mantle Sepolia
|
|
69
|
+
- **On-Chain Identity**: Every agent has a permanent contract address on Mantle blockchain
|
|
70
|
+
- **DEX Integration**: Agents execute real trades on Mantle-compatible decentralized exchanges
|
|
71
|
+
- **Factory Pattern**: Centralized `AgentFactory` contract manages all agent deployments
|
|
72
|
+
- **Blockchain-Backed Registry**: Agent addresses persist on-chain, surviving backend failures
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
### Initialize Repository for Mantle Deployment
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git mantle-agent init
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The initialization process:
|
|
83
|
+
- Creates `.gitagent.json` configuration file in your repository root
|
|
84
|
+
- Prompts for GitHub repository URL (used for webhook auto-configuration)
|
|
85
|
+
- Provides step-by-step instructions for connecting to MantlePush backend
|
|
86
|
+
- Guides you through secret management and first deployment workflow
|
|
87
|
+
|
|
88
|
+
### Configure Agent Secrets
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git mantle-agent secrets set GROQ_API_KEY=sk-your-key-here
|
|
92
|
+
git mantle-agent secrets set AGENT_PRIVATE_KEY=0x...
|
|
93
|
+
git mantle-agent secrets set AI_PROMPT="You are an aggressive trader"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Security Features:**
|
|
97
|
+
- Secrets are encrypted using AES-256 before storage
|
|
98
|
+
- Each Git branch maintains its own isolated secret set
|
|
99
|
+
- Secrets are automatically injected as environment variables when agents deploy to Mantle
|
|
100
|
+
- Never stored in plaintext - all encryption handled by MantlePush backend
|
|
101
|
+
|
|
102
|
+
### Monitor Agent Performance
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git mantle-agent stats # View comprehensive performance metrics
|
|
106
|
+
git mantle-agent logs # Stream real-time agent decision logs
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The `stats` command displays:
|
|
110
|
+
- Total trading decisions made
|
|
111
|
+
- BUY vs HOLD signal breakdown
|
|
112
|
+
- Number of executed trades
|
|
113
|
+
- Success rate percentage
|
|
114
|
+
- Price statistics (average, min, max)
|
|
115
|
+
- Activity timeline (first/last decision timestamps)
|
|
116
|
+
|
|
117
|
+
### Compare Trading Strategies
|
|
118
|
+
|
|
119
|
+
Run parallel A/B tests by comparing different Git branches deployed as separate Mantle contracts:
|
|
120
|
+
```bash
|
|
121
|
+
git somnia-agent compare main aggressive
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Comparison Metrics:**
|
|
125
|
+
- Decision volume (total decisions per strategy)
|
|
126
|
+
- Signal distribution (BUY vs HOLD ratios)
|
|
127
|
+
- Trade execution counts
|
|
128
|
+
- Win rate percentages
|
|
129
|
+
- Average trading prices
|
|
130
|
+
- Performance winner analysis
|
|
131
|
+
|
|
132
|
+
Perfect for testing different AI prompts, trading thresholds, or risk parameters across multiple Mantle agent contracts simultaneously.
|
|
133
|
+
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
The CLI creates a `.gitagent.json` file in your repository root:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"repo_url": "https://github.com/username/repo.git"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
To test the CLI locally:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npm link
|
|
150
|
+
git mantle-agent --help
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## About MantlePush
|
|
154
|
+
|
|
155
|
+
MantlePush is a Git-native deployment platform built specifically for Mantle Network. It brings the simplicity of Vercel-style deployments to blockchain AI agents, making Mantle the easiest network to deploy autonomous agents on.
|
|
156
|
+
|
|
157
|
+
**Key Benefits:**
|
|
158
|
+
- ā” **30-second deployments** - From `git push` to live on-chain agent
|
|
159
|
+
- š **Branch-based A/B testing** - Test multiple strategies simultaneously
|
|
160
|
+
- š **Enterprise security** - Encrypted secrets, on-chain identity
|
|
161
|
+
- š **Real-time monitoring** - CLI and web dashboard for agent metrics
|
|
162
|
+
|
|
163
|
+
**Repository**: https://github.com/xaviersharwin10/somnia-git-agent
|
|
164
|
+
**Live Dashboard**: https://somnia-git-agent.onrender.com/dashboard
|
|
165
|
+
**Mantle Explorer**: https://sepolia.mantlescan.xyz
|
|
166
|
+
|
|
167
|
+
## Requirements
|
|
168
|
+
|
|
169
|
+
- **Node.js** 16+ (for CLI tool)
|
|
170
|
+
- **Git** repository (for version control)
|
|
171
|
+
- **MantlePush Backend** (deployed at https://somnia-git-agent.onrender.com or self-hosted)
|
|
172
|
+
- **Mantle Sepolia Testnet** access (for contract deployment)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
const shell = require('shelljs');
|
|
6
|
+
const { createPromptModule } = require('inquirer');
|
|
7
|
+
const prompt = createPromptModule();
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program.version('1.0.0');
|
|
14
|
+
|
|
15
|
+
// --- Configuration ---
|
|
16
|
+
// MantlePush backend API endpoint
|
|
17
|
+
// For local development, use http://localhost:3005
|
|
18
|
+
// For production deployments, use your deployed backend URL
|
|
19
|
+
const API_BASE_URL = 'https://somnia-git-agent.onrender.com'; // Production MantlePush backend
|
|
20
|
+
const CONFIG_FILE = '.gitagent.json';
|
|
21
|
+
|
|
22
|
+
// --- Helper Functions ---
|
|
23
|
+
|
|
24
|
+
// Reads the .gitagent.json file
|
|
25
|
+
function getConfig() {
|
|
26
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
27
|
+
console.error(chalk.red(`Error: This repository is not configured for MantlePush. Missing ${CONFIG_FILE}.`));
|
|
28
|
+
console.log(chalk.yellow('Run `git mantle-agent init` to initialize MantlePush in this repository.'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Gets the current git branch
|
|
36
|
+
function getCurrentBranch() {
|
|
37
|
+
const branch = shell.exec('git rev-parse --abbrev-ref HEAD', { silent: true }).stdout.trim();
|
|
38
|
+
if (!branch) {
|
|
39
|
+
console.error(chalk.red('Error: Could not determine git branch.'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
return branch;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Calculate branch_hash (same as backend)
|
|
46
|
+
const { ethers } = require('ethers');
|
|
47
|
+
|
|
48
|
+
function calculateBranchHash(repo_url, branch_name) {
|
|
49
|
+
return ethers.id(repo_url + "/" + branch_name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Helper function to fetch stats for a specific branch
|
|
53
|
+
async function getStats(repo_url, branch_name) {
|
|
54
|
+
try {
|
|
55
|
+
const branch_hash = calculateBranchHash(repo_url, branch_name);
|
|
56
|
+
const url = `${API_BASE_URL}/api/stats/${branch_hash}`;
|
|
57
|
+
const { data } = await axios.get(url);
|
|
58
|
+
return { ...data, branch_name, repo_url };
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const errorMsg = err.response?.data?.error || err.message;
|
|
61
|
+
if (err.response?.status === 404) {
|
|
62
|
+
console.error(chalk.red(`Agent not found for branch "${branch_name}"`));
|
|
63
|
+
console.log(chalk.yellow(` ā Make sure you've pushed this branch: ${chalk.cyan(`git push origin ${branch_name}`)}`));
|
|
64
|
+
console.log(chalk.yellow(` ā The backend webhook will deploy it automatically`));
|
|
65
|
+
} else {
|
|
66
|
+
console.error(chalk.red(`Error fetching stats for ${branch_name}: ${errorMsg}`));
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- CLI Commands ---
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 1. INIT
|
|
76
|
+
* Initializes the project by creating .gitagent.json
|
|
77
|
+
*/
|
|
78
|
+
program
|
|
79
|
+
.command('init')
|
|
80
|
+
.description('Initialize MantlePush deployment pipeline for this repository')
|
|
81
|
+
.action(async () => {
|
|
82
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
83
|
+
console.log(chalk.yellow(`This project is already initialized.`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const answers = await prompt([
|
|
88
|
+
{
|
|
89
|
+
type: 'input',
|
|
90
|
+
name: 'repo_url',
|
|
91
|
+
message: 'What is your GitHub repository URL (e.g., https://github.com/user/repo.git)?',
|
|
92
|
+
default: shell.exec('git remote get-url origin', { silent: true }).stdout.trim(),
|
|
93
|
+
}
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
if (!answers.repo_url) {
|
|
97
|
+
console.error(chalk.red('Error: Repository URL is required.'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const config = { repo_url: answers.repo_url };
|
|
102
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
103
|
+
|
|
104
|
+
console.log(chalk.green(`ā
${CONFIG_FILE} created.`));
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log(chalk.bold('š Next Steps:'));
|
|
107
|
+
console.log('');
|
|
108
|
+
|
|
109
|
+
// Show OAuth URL for automatic setup
|
|
110
|
+
const repoUrl = answers.repo_url;
|
|
111
|
+
const oauthUrl = `https://somnia-git-agent.onrender.com/auth/github?repo_url=${encodeURIComponent(repoUrl)}`;
|
|
112
|
+
|
|
113
|
+
console.log(chalk.cyan('š Option A: Automatic Webhook Configuration (Recommended)'));
|
|
114
|
+
console.log(` Visit: ${chalk.underline(oauthUrl)}`);
|
|
115
|
+
console.log(` Authorize GitHub to automatically set up deployment webhooks`);
|
|
116
|
+
console.log('');
|
|
117
|
+
|
|
118
|
+
console.log(chalk.yellow('āļø Option B: Manual Webhook Configuration'));
|
|
119
|
+
console.log(` Navigate to: GitHub ā ${answers.repo_url.split('/').slice(-2).join('/')} ā Settings ā Webhooks`);
|
|
120
|
+
console.log(` Webhook URL: ${chalk.cyan('https://somnia-git-agent.onrender.com/webhook/github/push')}`);
|
|
121
|
+
console.log(` Content type: ${chalk.cyan('application/json')}`);
|
|
122
|
+
console.log(` Events: ${chalk.cyan('Just the push event')}`);
|
|
123
|
+
console.log('');
|
|
124
|
+
|
|
125
|
+
console.log(chalk.bold('š Configure Agent Secrets:'));
|
|
126
|
+
console.log(` ${chalk.cyan('git mantle-agent secrets set GROQ_API_KEY=your-key-here')}`);
|
|
127
|
+
console.log(` ${chalk.cyan('git mantle-agent secrets set AGENT_PRIVATE_KEY=0x...')}`);
|
|
128
|
+
console.log(` These secrets are encrypted and securely stored for each branch`);
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
console.log(chalk.bold('š Deploy to Mantle Sepolia:'));
|
|
132
|
+
console.log(` ${chalk.cyan('git push origin main')}`);
|
|
133
|
+
console.log(` Each push automatically deploys a new smart contract on Mantle Sepolia testnet`);
|
|
134
|
+
console.log(` Your agent will be live on-chain within 30 seconds!`);
|
|
135
|
+
console.log('');
|
|
136
|
+
|
|
137
|
+
console.log(chalk.bold('š Monitor Your Agents:'));
|
|
138
|
+
console.log(` ${chalk.cyan('git mantle-agent stats')} - View real-time performance metrics`);
|
|
139
|
+
console.log(` ${chalk.cyan('git mantle-agent logs')} - Stream live agent decision logs`);
|
|
140
|
+
console.log(` Web Dashboard: ${chalk.underline('https://somnia-git-agent.onrender.com/dashboard')}`);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 2. SECRETS - Create a command group for secrets
|
|
145
|
+
*/
|
|
146
|
+
const secretsCommand = program
|
|
147
|
+
.command('secrets')
|
|
148
|
+
.description('Manage secrets for the current branch');
|
|
149
|
+
|
|
150
|
+
// SECRETS SET - Set a secret
|
|
151
|
+
secretsCommand
|
|
152
|
+
.command('set <KEY_VALUE>')
|
|
153
|
+
.description('Set a secret for the current branch (e.g., KEY=VALUE)')
|
|
154
|
+
.action(async (keyValue) => {
|
|
155
|
+
// Handle the case where commander might parse this incorrectly
|
|
156
|
+
const fullCommand = process.argv.slice(2).join(' ');
|
|
157
|
+
const match = fullCommand.match(/secrets set (.+)/);
|
|
158
|
+
|
|
159
|
+
if (!match) {
|
|
160
|
+
console.error(chalk.red('Error: Invalid format. Use KEY=VALUE'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const keyValueStr = match[1];
|
|
165
|
+
const [key, ...valueParts] = keyValueStr.split('=');
|
|
166
|
+
const value = valueParts.join('=');
|
|
167
|
+
|
|
168
|
+
if (!key || !value) {
|
|
169
|
+
console.error(chalk.red('Error: Invalid format. Use KEY=VALUE'));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const config = getConfig();
|
|
174
|
+
const branch_name = getCurrentBranch();
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
console.log(chalk.cyan(`Setting secret ${key} for branch ${branch_name}...`));
|
|
178
|
+
await axios.post(`${API_BASE_URL}/api/secrets`, {
|
|
179
|
+
repo_url: config.repo_url,
|
|
180
|
+
branch_name: branch_name,
|
|
181
|
+
key: key,
|
|
182
|
+
value: value,
|
|
183
|
+
});
|
|
184
|
+
console.log(chalk.green(`ā
Secret ${key} set.`));
|
|
185
|
+
} catch (err) {
|
|
186
|
+
console.error(chalk.red(`Error setting secret: ${err.response?.data?.error || err.message}`));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// SECRETS CHECK - Check which secrets are set
|
|
191
|
+
secretsCommand
|
|
192
|
+
.command('check')
|
|
193
|
+
.description('Check which required secrets are set for the current branch')
|
|
194
|
+
.action(async () => {
|
|
195
|
+
const config = getConfig();
|
|
196
|
+
const branch_name = getCurrentBranch();
|
|
197
|
+
const branch_hash = calculateBranchHash(config.repo_url, branch_name);
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
console.log(chalk.cyan(`š Checking secrets for branch: ${chalk.bold(branch_name)}...`));
|
|
201
|
+
const { data } = await axios.get(`${API_BASE_URL}/api/secrets/check/${branch_hash}`);
|
|
202
|
+
|
|
203
|
+
console.log(chalk.bold(`\n--- Secrets Status for ${branch_name} ---`));
|
|
204
|
+
|
|
205
|
+
// Required secrets
|
|
206
|
+
console.log(chalk.bold('\nš Required Secrets:'));
|
|
207
|
+
data.secrets.required.forEach(secret => {
|
|
208
|
+
const status = secret.set ? chalk.green('ā
Set') : chalk.red('ā Missing');
|
|
209
|
+
console.log(` ${status} ${chalk.bold(secret.key)}`);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Optional secrets
|
|
213
|
+
if (data.secrets.optional.length > 0) {
|
|
214
|
+
console.log(chalk.bold('\nāļø Optional Secrets:'));
|
|
215
|
+
data.secrets.optional.forEach(secret => {
|
|
216
|
+
const status = secret.set ? chalk.cyan('ā Set') : chalk.gray('ā Not set');
|
|
217
|
+
console.log(` ${status} ${secret.key}`);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Overall status
|
|
222
|
+
console.log(chalk.bold('\nš Status:'));
|
|
223
|
+
if (data.all_required_set) {
|
|
224
|
+
console.log(chalk.green(` ā
All required secrets are set! Agent is ready to run.`));
|
|
225
|
+
} else {
|
|
226
|
+
console.log(chalk.red(` ā Missing required secrets: ${chalk.bold(data.missing.join(', '))}`));
|
|
227
|
+
console.log(chalk.yellow(`\nš” Set missing secrets with:`));
|
|
228
|
+
data.missing.forEach(key => {
|
|
229
|
+
console.log(chalk.cyan(` git mantle-agent secrets set ${key}=<your-value>`));
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (err.response?.status === 404) {
|
|
235
|
+
console.error(chalk.red(`Agent not found for branch "${branch_name}"`));
|
|
236
|
+
console.log(chalk.yellow(` ā Make sure you've pushed this branch: ${chalk.cyan(`git push origin ${branch_name}`)}`));
|
|
237
|
+
} else {
|
|
238
|
+
console.error(chalk.red(`Error checking secrets: ${err.response?.data?.error || err.message}`));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 4. STATS
|
|
245
|
+
* Gets stats for the current branch
|
|
246
|
+
*/
|
|
247
|
+
program
|
|
248
|
+
.command('stats')
|
|
249
|
+
.description('View real-time performance metrics for your Mantle agent')
|
|
250
|
+
.action(async () => {
|
|
251
|
+
const config = getConfig();
|
|
252
|
+
const branch_name = getCurrentBranch();
|
|
253
|
+
|
|
254
|
+
console.log(chalk.cyan(`š Fetching stats for ${branch_name}...`));
|
|
255
|
+
const result = await getStats(config.repo_url, branch_name);
|
|
256
|
+
|
|
257
|
+
if (!result) {
|
|
258
|
+
console.log(chalk.yellow(`\nā ļø Could not fetch stats for "${branch_name}"`));
|
|
259
|
+
console.log(chalk.yellow(` The agent may not be deployed yet, or there was an error.`));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (result && result.stats) {
|
|
264
|
+
const s = result.stats;
|
|
265
|
+
const totalDecisions = s.total_decisions || 0;
|
|
266
|
+
|
|
267
|
+
console.log(chalk.bold(`\n--- Mantle Agent Performance: ${branch_name} ---`));
|
|
268
|
+
console.log(chalk.green(` Total Decisions: ${totalDecisions}`));
|
|
269
|
+
console.log(chalk.cyan(` BUY Signals: ${s.buy_count || 0}`));
|
|
270
|
+
console.log(chalk.yellow(` HOLD Signals: ${s.hold_count || 0}`));
|
|
271
|
+
console.log(chalk.magenta(` Trades Executed: ${s.trades_executed || 0}`));
|
|
272
|
+
|
|
273
|
+
if (totalDecisions === 0) {
|
|
274
|
+
console.log(chalk.yellow(`\nā ļø No trading decisions recorded yet.`));
|
|
275
|
+
console.log(chalk.yellow(` ā Check agent status: ${chalk.cyan('git mantle-agent logs')}`));
|
|
276
|
+
console.log(chalk.yellow(` ā Verify the agent process is running on MantlePush backend`));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (s.avg_price) {
|
|
281
|
+
console.log(`\n Price Statistics:`);
|
|
282
|
+
console.log(` Average: $${parseFloat(s.avg_price).toFixed(4)}`);
|
|
283
|
+
console.log(` Min: $${parseFloat(s.min_price).toFixed(4)}`);
|
|
284
|
+
console.log(` Max: $${parseFloat(s.max_price).toFixed(4)}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (s.first_decision && s.last_decision) {
|
|
288
|
+
console.log(`\n Activity:`);
|
|
289
|
+
console.log(` First Decision: ${s.first_decision}`);
|
|
290
|
+
console.log(` Last Decision: ${s.last_decision}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (s.trades_executed > 0 && totalDecisions > 0) {
|
|
294
|
+
const successRate = ((s.trades_executed / totalDecisions) * 100).toFixed(1);
|
|
295
|
+
console.log(chalk.green(`\n Success Rate: ${successRate}%`));
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
console.log(chalk.yellow('No performance metrics available yet. The agent needs to make trading decisions first.'));
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 5. LOGS
|
|
304
|
+
* Gets logs for the current branch
|
|
305
|
+
*/
|
|
306
|
+
program
|
|
307
|
+
.command('logs')
|
|
308
|
+
.description('Stream real-time logs from your Mantle agent process')
|
|
309
|
+
.action(async () => {
|
|
310
|
+
const config = getConfig();
|
|
311
|
+
const branch_name = getCurrentBranch();
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
console.log(chalk.cyan(`Fetching logs for ${branch_name}...`));
|
|
315
|
+
const url = `${API_BASE_URL}/api/logs/${encodeURIComponent(config.repo_url)}/${encodeURIComponent(branch_name)}`;
|
|
316
|
+
const { data } = await axios.get(url);
|
|
317
|
+
|
|
318
|
+
console.log(chalk.bold(`--- Recent Agent Logs: ${branch_name} (Last 50 entries) ---`));
|
|
319
|
+
if (data.logs && data.logs.length > 0) {
|
|
320
|
+
data.logs.forEach(line => console.log(line));
|
|
321
|
+
} else {
|
|
322
|
+
console.log(chalk.yellow('No logs found.'));
|
|
323
|
+
}
|
|
324
|
+
} catch (err) {
|
|
325
|
+
console.error(chalk.red(`Error fetching logs: ${err.response?.data?.error || err.message}`));
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 6. COMPARE
|
|
331
|
+
* Compares two branches side-by-side
|
|
332
|
+
*/
|
|
333
|
+
program
|
|
334
|
+
.command('compare <branch1> <branch2>')
|
|
335
|
+
.description('Compare performance metrics between two agent strategies on Mantle')
|
|
336
|
+
.action(async (branch1, branch2) => {
|
|
337
|
+
const config = getConfig();
|
|
338
|
+
|
|
339
|
+
console.log(chalk.cyan(`š Comparing Mantle agent strategies: ${chalk.bold(branch1)} vs ${chalk.bold(branch2)}...`));
|
|
340
|
+
|
|
341
|
+
const [result1, result2] = await Promise.all([
|
|
342
|
+
getStats(config.repo_url, branch1),
|
|
343
|
+
getStats(config.repo_url, branch2)
|
|
344
|
+
]);
|
|
345
|
+
|
|
346
|
+
if (!result1 || !result2) {
|
|
347
|
+
console.error(chalk.red('Could not fetch stats for comparison.'));
|
|
348
|
+
if (!result1) {
|
|
349
|
+
console.log(chalk.yellow(` ${branch1}: Agent not found or error occurred`));
|
|
350
|
+
console.log(chalk.yellow(` ā Make sure you've pushed: ${chalk.cyan(`git push origin ${branch1}`)}`));
|
|
351
|
+
}
|
|
352
|
+
if (!result2) {
|
|
353
|
+
console.log(chalk.yellow(` ${branch2}: Agent not found or error occurred`));
|
|
354
|
+
console.log(chalk.yellow(` ā Make sure you've pushed: ${chalk.cyan(`git push origin ${branch2}`)}`));
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!result1.stats || !result2.stats) {
|
|
360
|
+
console.log(chalk.yellow('\nā ļø One or both agents have no metrics yet.'));
|
|
361
|
+
if (!result1.stats) console.log(chalk.yellow(` ${branch1}: Waiting for first decision...`));
|
|
362
|
+
if (!result2.stats) console.log(chalk.yellow(` ${branch2}: Waiting for first decision...`));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const s1 = result1.stats;
|
|
367
|
+
const s2 = result2.stats;
|
|
368
|
+
|
|
369
|
+
// Helper function to strip ANSI codes for width calculation
|
|
370
|
+
const stripAnsi = (str) => str.replace(/\u001b\[[0-9;]*m/g, '');
|
|
371
|
+
|
|
372
|
+
// Helper function to pad string accounting for ANSI codes
|
|
373
|
+
const padWithAnsi = (str, width) => {
|
|
374
|
+
const visibleLength = stripAnsi(str).length;
|
|
375
|
+
const padding = Math.max(0, width - visibleLength);
|
|
376
|
+
return str + ' '.repeat(padding);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
console.log(chalk.bold('\nāāāāāāāāāāāāāāāāāāāāāāā¦āāāāāāāāāāāāāāāāāāāāāāāāāāāā¦āāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
380
|
+
const titleText = ' Mantle Agent Strategy Comparison';
|
|
381
|
+
const titlePadding = 78 - titleText.length; // 77 total width minus title and borders
|
|
382
|
+
console.log(chalk.bold(`ā${titleText}${' '.repeat(titlePadding)}ā`));
|
|
383
|
+
console.log(chalk.bold('ā āāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāā£'));
|
|
384
|
+
|
|
385
|
+
// Create comparison table
|
|
386
|
+
const metrics = [
|
|
387
|
+
{ label: 'Total Decisions', v1: s1.total_decisions || 0, v2: s2.total_decisions || 0, format: (v) => v.toString() },
|
|
388
|
+
{ label: 'BUY Signals', v1: s1.buy_count || 0, v2: s2.buy_count || 0, format: (v) => chalk.cyan(v.toString()) },
|
|
389
|
+
{ label: 'HOLD Signals', v1: s1.hold_count || 0, v2: s2.hold_count || 0, format: (v) => chalk.yellow(v.toString()) },
|
|
390
|
+
{ label: 'Trades Executed', v1: s1.trades_executed || 0, v2: s2.trades_executed || 0, format: (v) => chalk.magenta(v.toString()) },
|
|
391
|
+
{ label: 'Avg Price', v1: s1.avg_price || 0, v2: s2.avg_price || 0, format: (v) => `$${parseFloat(v).toFixed(4)}` },
|
|
392
|
+
{ label: 'Success Rate',
|
|
393
|
+
v1: s1.total_decisions ? ((s1.trades_executed / s1.total_decisions) * 100).toFixed(1) : '0.0',
|
|
394
|
+
v2: s2.total_decisions ? ((s2.trades_executed / s2.total_decisions) * 100).toFixed(1) : '0.0',
|
|
395
|
+
format: (v) => chalk.green(`${v}%`)
|
|
396
|
+
},
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
// Header row
|
|
400
|
+
const headerMetric = padWithAnsi('Metric', 19);
|
|
401
|
+
const header1 = padWithAnsi(chalk.bold(branch1), 27);
|
|
402
|
+
const header2 = padWithAnsi(chalk.bold(branch2), 27);
|
|
403
|
+
console.log(`ā ${headerMetric}ā ${header1}ā ${header2}ā`);
|
|
404
|
+
console.log(chalk.bold('ā āāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā£'));
|
|
405
|
+
|
|
406
|
+
// Data rows
|
|
407
|
+
metrics.forEach((m, idx) => {
|
|
408
|
+
const label = padWithAnsi(m.label, 19);
|
|
409
|
+
const v1Str = padWithAnsi(m.format(m.v1), 27);
|
|
410
|
+
const v2Str = padWithAnsi(m.format(m.v2), 27);
|
|
411
|
+
console.log(`ā ${label}ā ${v1Str}ā ${v2Str}ā`);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
console.log(chalk.bold('āāāāāāāāāāāāāāāāāāāāāāā©āāāāāāāāāāāāāāāāāāāāāāāāāāāā©āāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
415
|
+
|
|
416
|
+
// Determine winner
|
|
417
|
+
console.log('\n' + chalk.bold('š Performance Analysis:'));
|
|
418
|
+
if ((s1.trades_executed || 0) > (s2.trades_executed || 0)) {
|
|
419
|
+
console.log(chalk.green(` ${branch1} has executed more trades`));
|
|
420
|
+
} else if ((s2.trades_executed || 0) > (s1.trades_executed || 0)) {
|
|
421
|
+
console.log(chalk.green(` ${branch2} has executed more trades`));
|
|
422
|
+
} else {
|
|
423
|
+
console.log(chalk.yellow(` Both strategies show similar trade execution patterns`));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (s1.total_decisions && s2.total_decisions) {
|
|
427
|
+
const rate1 = (s1.trades_executed / s1.total_decisions) * 100;
|
|
428
|
+
const rate2 = (s2.trades_executed / s2.total_decisions) * 100;
|
|
429
|
+
if (rate1 > rate2) {
|
|
430
|
+
console.log(chalk.green(` ${branch1} has better success rate (${rate1.toFixed(1)}% vs ${rate2.toFixed(1)}%)`));
|
|
431
|
+
} else if (rate2 > rate1) {
|
|
432
|
+
console.log(chalk.green(` ${branch2} has better success rate (${rate2.toFixed(1)}% vs ${rate1.toFixed(1)}%)`));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// --- Parse and Run ---
|
|
438
|
+
program.parse(process.argv);
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marshalz/git-mantle-agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for deploying and managing AI agents on Mantle Sepolia via Git workflows",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"readme": "README.md",
|
|
7
|
+
"bin": {
|
|
8
|
+
"git-mantle-agent": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/xaviersharwin10/somnia-git-agent.git",
|
|
13
|
+
"directory": "git-agent-cli"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"git",
|
|
17
|
+
"agent",
|
|
18
|
+
"cli",
|
|
19
|
+
"ai",
|
|
20
|
+
"trading",
|
|
21
|
+
"blockchain",
|
|
22
|
+
"mantle",
|
|
23
|
+
"web3",
|
|
24
|
+
"defi",
|
|
25
|
+
"autonomous-agents"
|
|
26
|
+
],
|
|
27
|
+
"author": "marshal.25ec@licet.ac.in",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"axios": "^1.13.1",
|
|
34
|
+
"chalk": "^4.1.2",
|
|
35
|
+
"commander": "^14.0.2",
|
|
36
|
+
"ethers": "^6.15.0",
|
|
37
|
+
"inquirer": "^12.10.0",
|
|
38
|
+
"shelljs": "^0.10.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=16.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|