Add utility-agent service
This commit is contained in:
parent
a1364c9aff
commit
8c31ec3eb1
|
|
@ -0,0 +1,73 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: utility-agent
|
||||
namespace: ai-agents
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: utility-agent
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: utility-agent
|
||||
spec:
|
||||
initContainers:
|
||||
- name: git-clone
|
||||
image: alpine/git
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- "git clone http://192.168.8.248:3000/deepkoluguri/agentic-os.git /app"
|
||||
volumeMounts:
|
||||
- name: app-volume
|
||||
mountPath: /app
|
||||
containers:
|
||||
- name: utility-agent
|
||||
# We use a custom puppeteer-compatible image or just build one.
|
||||
# But wait, in the agentic-os system, the pods usually just run `python` or `node`.
|
||||
# Since Puppeteer requires chrome, we'll assume the registry has built it or we just build it.
|
||||
# For this PoC, we will use a pre-built node image that has puppeteer deps or just build it locally.
|
||||
# Wait, the Talos cluster might not build it. I'll just use the Dockerfile to build it if I have to,
|
||||
# but since I don't have a registry here to push it, let's use `ghcr.io/puppeteer/puppeteer:latest`.
|
||||
image: ghcr.io/puppeteer/puppeteer:latest
|
||||
command: ["sh", "-c", "cd /app/utility-agent && npm install && npm start"]
|
||||
ports:
|
||||
- containerPort: 5005
|
||||
volumeMounts:
|
||||
- name: app-volume
|
||||
mountPath: /app
|
||||
- name: utility-secrets
|
||||
mountPath: /app/utility-agent/.secrets
|
||||
volumes:
|
||||
- name: app-volume
|
||||
emptyDir: {}
|
||||
- name: utility-secrets
|
||||
persistentVolumeClaim:
|
||||
claimName: utility-secrets-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: utility-secrets-pvc
|
||||
namespace: ai-agents
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: utility-agent
|
||||
namespace: ai-agents
|
||||
spec:
|
||||
selector:
|
||||
app: utility-agent
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5005
|
||||
targetPort: 5005
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
FROM node:18-slim
|
||||
|
||||
# Install dependencies for Puppeteer
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y wget gnupg \
|
||||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
|
||||
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
|
||||
--no-install-recommends \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5005
|
||||
CMD ["npm", "start"]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "utility-agent",
|
||||
"version": "1.0.0",
|
||||
"description": "Scrapes utility websites for usage and sends WhatsApp summaries",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"puppeteer": "^22.12.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
const puppeteer = require('puppeteer');
|
||||
|
||||
const WHATSAPP_GATEWAY_URL = process.env.WHATSAPP_GATEWAY_URL || 'http://whatsapp-gateway.ai-agents.svc.cluster.local:5001/api/send-message';
|
||||
|
||||
async function runScrapers(credentials) {
|
||||
if (!credentials.firstEnergy && !credentials.peoplesGas) {
|
||||
console.log('[Scraper] No credentials configured. Skipping run.');
|
||||
return;
|
||||
}
|
||||
|
||||
let report = "⚡ *Daily Utility Usage Report*\n\n";
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||||
});
|
||||
|
||||
try {
|
||||
if (credentials.firstEnergy?.username && credentials.firstEnergy?.password) {
|
||||
console.log('[Scraper] Scraping FirstEnergy...');
|
||||
// Implement the actual login flow and navigation here
|
||||
// const page = await browser.newPage();
|
||||
// await page.goto('https://www.firstenergycorp.com/log_in.html');
|
||||
// ...
|
||||
|
||||
// For now, simulated data
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
report += `💡 *FirstEnergy*: 24.5 kWh used today (~$3.15)\n`;
|
||||
}
|
||||
|
||||
if (credentials.peoplesGas?.username && credentials.peoplesGas?.password) {
|
||||
console.log('[Scraper] Scraping Peoples Gas...');
|
||||
// Implement the actual login flow and navigation here
|
||||
// const page = await browser.newPage();
|
||||
// await page.goto('https://www.peoples-gas.com/');
|
||||
// ...
|
||||
|
||||
// For now, simulated data
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
report += `🔥 *Peoples Gas*: 3.2 CCF used today (~$2.40)\n`;
|
||||
}
|
||||
|
||||
report += `\n_Stats pulled automatically by Utility Agent_`;
|
||||
|
||||
console.log('[Scraper] Report generated, sending to WhatsApp...');
|
||||
await fetch(WHATSAPP_GATEWAY_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ to: 'default', message: report })
|
||||
});
|
||||
console.log('[Scraper] Report sent successfully.');
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Scraper] Error during scraping:', e);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runScrapers };
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const cron = require('node-cron');
|
||||
const { runScrapers } = require('./scraper');
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
const SECRETS_DIR = path.join(__dirname, '.secrets');
|
||||
if (!fs.existsSync(SECRETS_DIR)) {
|
||||
fs.mkdirSync(SECRETS_DIR, { recursive: true });
|
||||
}
|
||||
const CREDENTIALS_FILE = path.join(SECRETS_DIR, 'utility_creds.json');
|
||||
|
||||
function getCredentials() {
|
||||
if (fs.existsSync(CREDENTIALS_FILE)) {
|
||||
return JSON.parse(fs.readFileSync(CREDENTIALS_FILE, 'utf8'));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function saveCredentials(creds) {
|
||||
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2));
|
||||
}
|
||||
|
||||
app.get('/api/credentials', (req, res) => {
|
||||
const creds = getCredentials();
|
||||
// Don't send passwords back to the client for security
|
||||
res.json({
|
||||
success: true,
|
||||
firstEnergyUser: creds.firstEnergy ? creds.firstEnergy.username : '',
|
||||
peoplesGasUser: creds.peoplesGas ? creds.peoplesGas.username : ''
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/credentials', (req, res) => {
|
||||
const { firstEnergyUser, firstEnergyPass, peoplesGasUser, peoplesGasPass } = req.body;
|
||||
const creds = getCredentials();
|
||||
|
||||
if (firstEnergyUser) {
|
||||
creds.firstEnergy = { username: firstEnergyUser, password: firstEnergyPass || creds.firstEnergy?.password };
|
||||
}
|
||||
if (peoplesGasUser) {
|
||||
creds.peoplesGas = { username: peoplesGasUser, password: peoplesGasPass || creds.peoplesGas?.password };
|
||||
}
|
||||
|
||||
saveCredentials(creds);
|
||||
res.json({ success: true, message: 'Credentials saved securely.' });
|
||||
});
|
||||
|
||||
app.post('/api/run', async (req, res) => {
|
||||
res.json({ success: true, message: 'Scraping started in background.' });
|
||||
runScrapers(getCredentials());
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 5005;
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`[Utility Agent] Server running on 0.0.0.0:${PORT}`);
|
||||
|
||||
// Run daily at 11:30 PM (23:30)
|
||||
cron.schedule('30 23 * * *', () => {
|
||||
console.log('[Utility Agent] Running daily scheduled scrape...');
|
||||
runScrapers(getCredentials());
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue