Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"name": "pagra-ai",
"version": "1.0.0",
"type": "module",
"main": "src/server.js",
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js",
Expand All @@ -22,7 +26,7 @@
"description": "",
"dependencies": {
"@anthropic-ai/sdk": "^0.27.0",
"@modelcontextprotocol/sdk": "^1.12.1",
"@modelcontextprotocol/sdk": "^1.12.3",
"@supabase/supabase-js": "^2.39.0",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
Expand Down
16 changes: 5 additions & 11 deletions src/config/mcp.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const Anthropic = require('@anthropic-ai/sdk');
import Anthropic from '@anthropic-ai/sdk';

// Initialize Anthropic client
const anthropic = new Anthropic({
export const anthropic = new Anthropic({
apiKey: process.env.MCP_PRIVATE_KEY,
});

// Define MCP tools for wallet operations
const WALLET_TOOLS = [
export const WALLET_TOOLS = [
{
name: "create_wallet",
description: "Create a new wallet account for a user with their phone number and PIN",
Expand Down Expand Up @@ -126,7 +126,7 @@ const WALLET_TOOLS = [
];

// System prompt for the MCP assistant
const SYSTEM_PROMPT = `You are a helpful Starknet wallet assistant powered by Pagra AI. You help users manage their cryptocurrency wallets through natural language conversations.
export const SYSTEM_PROMPT = `You are a helpful Starknet wallet assistant powered by Pagra AI. You help users manage their cryptocurrency wallets through natural language conversations.

You have access to the following wallet operations:
1. Create a new wallet account
Expand Down Expand Up @@ -182,10 +182,4 @@ When a user tries to transfer/send funds but gets "Account not yet deployed" err
- Explain they need to deploy first
- Ask if they want you to deploy it for them
- If they agree, ask for their PIN and deploy the account
- Then proceed with their original request`;

module.exports = {
anthropic,
WALLET_TOOLS,
SYSTEM_PROMPT
};
- Then proceed with their original request`;
34 changes: 14 additions & 20 deletions src/config/supabase.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
const { createClient } = require('@supabase/supabase-js');
import { createClient } from '@supabase/supabase-js';
import dotenv from 'dotenv';

dotenv.config();

// Supabase configuration
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseKey) {
console.error('❌ Missing Supabase environment variables');
console.error('Please set SUPABASE_URL and SUPABASE_ANON_KEY in your .env file');
process.exit(1);
throw new Error('Missing Supabase credentials. Please check your environment variables.');
}

// Create Supabase client
const supabase = createClient(supabaseUrl, supabaseKey);
export const supabase = createClient(supabaseUrl, supabaseKey);

// Test connection
async function testConnection() {
export const testConnection = async () => {
try {
const { data, error } = await supabase.from('test').select('*').limit(1);
if (error && error.code !== 'PGRST116') { // PGRST116 is "relation does not exist"
console.error('❌ Supabase connection failed:', error.message);
return false;
const { data, error } = await supabase.from('users').select('count').limit(1);

if (error) {
throw error;
}

console.log('✅ Supabase connection successful');
return true;
} catch (error) {
console.error('❌ Supabase connection error:', error.message);
return false;
console.error('❌ Supabase connection failed:', error.message);
throw error;
}
}

module.exports = {
supabase,
testConnection
};
141 changes: 13 additions & 128 deletions src/config/swagger.js
Original file line number Diff line number Diff line change
@@ -1,147 +1,32 @@
const swaggerJsdoc = require('swagger-jsdoc');
import swaggerJsdoc from 'swagger-jsdoc';

const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Pagra AI - Starknet Wallet API',
version: '1.0.0',
description: 'API for managing Starknet wallets, transfers, and transactions',
contact: {
name: 'Pagra AI',
email: 'support@pagra.ai'
},
license: {
name: 'MIT',
url: 'https://opensource.org/licenses/MIT'
}
description: 'API documentation for Pagra AI Starknet Wallet service',
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
url: process.env.API_URL || 'http://localhost:3000',
description: 'Development server',
},
{
url: 'https://api.pagra.ai',
description: 'Production server'
}
],
components: {
schemas: {
User: {
type: 'object',
properties: {
phone_number: {
type: 'string',
description: 'User phone number',
example: '1234567890'
},
ozcontractaddress: {
type: 'string',
description: 'Starknet wallet address',
example: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
},
isdeployed: {
type: 'boolean',
description: 'Whether the account is deployed',
example: true
}
}
},
Transaction: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid',
description: 'Transaction ID'
},
sender_phone_number: {
type: 'string',
description: 'Sender phone number'
},
receiver_phone_number: {
type: 'string',
description: 'Receiver phone number (optional)'
},
receiver_address: {
type: 'string',
description: 'Receiver wallet address'
},
sender_address: {
type: 'string',
description: 'Sender wallet address'
},
amount: {
type: 'number',
description: 'Transfer amount in STRK'
},
transaction_hash: {
type: 'string',
description: 'Blockchain transaction hash'
},
status: {
type: 'string',
enum: ['pending', 'completed', 'failed'],
description: 'Transaction status'
},
created_at: {
type: 'string',
format: 'date-time',
description: 'Transaction creation timestamp'
}
}
},
Error: {
type: 'object',
properties: {
message: {
type: 'string',
description: 'Error message'
},
status: {
type: 'boolean',
example: false
}
}
},
Success: {
type: 'object',
properties: {
message: {
type: 'string',
description: 'Success message'
},
status: {
type: 'boolean',
example: true
},
data: {
type: 'object',
description: 'Response data (optional)'
}
}
}
},
securitySchemes: {
phoneNumber: {
type: 'apiKey',
in: 'body',
name: 'phone_number',
description: 'User phone number for authentication'
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
pin: {
type: 'apiKey',
in: 'body',
name: 'pin',
description: '6-digit PIN for authentication'
}
}
}
},
},
},
apis: ['./src/server.js', './src/docs/api-docs.js'], // Include both files
apis: ['./src/routes/*.js'], // Path to the API routes
};

const specs = swaggerJsdoc(options);
const swaggerSpecs = swaggerJsdoc(options);

module.exports = specs;
export default swaggerSpecs;
20 changes: 6 additions & 14 deletions src/config/telegram.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
const TelegramBot = require('node-telegram-bot-api');
import TelegramBot from 'node-telegram-bot-api';

// Telegram Bot Configuration
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
export const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;

if (!TELEGRAM_BOT_TOKEN) {
console.warn('⚠️ TELEGRAM_BOT_TOKEN not found in environment variables');
}

// Create bot instance
const bot = TELEGRAM_BOT_TOKEN ? new TelegramBot(TELEGRAM_BOT_TOKEN, { polling: false }) : null;
export const bot = TELEGRAM_BOT_TOKEN ? new TelegramBot(TELEGRAM_BOT_TOKEN, { polling: false }) : null;

// Simple message formatting (minimal, since we want direct MCP responses)
const formatMessage = {
export const formatMessage = {
error: (message) => `❌ ${message}`,
success: (message) => `✅ ${message}`
};

// Extract phone number from Telegram user
const extractPhoneNumber = (telegramUser) => {
export const extractPhoneNumber = (telegramUser) => {
// For now, we'll use the Telegram user ID as phone number
// In production, you might want to implement phone verification
return `telegram_${telegramUser.id}`;
};

// Validate if user has provided phone number
const validatePhoneNumber = (phoneNumber) => {
export const validatePhoneNumber = (phoneNumber) => {
return phoneNumber && phoneNumber.startsWith('telegram_');
};

module.exports = {
bot,
formatMessage,
extractPhoneNumber,
validatePhoneNumber,
TELEGRAM_BOT_TOKEN
};
Loading