/**
* Pi DevOps β Optimized extension for CharityRight/QuikCue infrastructure
*
* Features:
* ποΈ Infra context β injects full topology into every AI prompt
* π‘οΈ Safety guard β confirms destructive ops before execution
* π Smart footer β model, context, cost, tool tally, server indicator
* π― Focus widget β keeps current task visible at all times
* β‘ /health β live container + service status check
* π /logs [svc] β tail logs for a service
* πΊοΈ /infra β print full infra topology
* π― /focus [task] β set/change the current task
* π /stop β abort current AI action
* π§ /ssh [cmd] β run a raw SSH command on the server
*
* Usage: pi -e extensions/pi-devops.ts
*/
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
import type { AssistantMessage } from "@mariozechner/pi-ai";
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { basename } from "node:path";
import { execSync } from "node:child_process";
import { applyExtensionDefaults } from "./themeMap.ts";
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Infra constants
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const SSH = `ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -p 22 root@159.195.60.33`;
const INCUS_CR = `${SSH} "incus exec cr-server-new --"`;
const INFRA_CONTEXT = `
You are Pi, a DevOps assistant managing live production infrastructure.
## Primary Server
- SSH: ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -p 22 root@159.195.60.33 ""
- HAProxy routes: charityright domains β cr-server-new, quikcue domains β qc-server-new
## Incus Containers (on primary server)
- cr-server-new (10.213.16.224) β CharityRight. Docker runs INSIDE here.
- qc-server-new (10.213.16.234) β QuikCue
- qc-server β STOPPED legacy
## Docker (inside cr-server-new only!)
- Run: ssh ... "incus exec cr-server-new -- docker "
- Postgres container: dokploy-migrated-cr-postgres-data
- DB: donation_warehouse
- Query: ssh ... "incus exec cr-server-new -- docker exec dokploy-migrated-cr-postgres-data psql -U postgres -d donation_warehouse -c \\"SELECT ...\\""
## Services
- /opt/ayn-antivirus β AYN antivirus scanner
- /opt/enthuse-db-sync-v2 β Enthuse donation sync
- /opt/launchgood-sync β LaunchGood sync
- /root/legacy-donation-system-laravel β CharityRight Laravel app
- /root/redis-v2 β Redis
## Critical Rules
- NEVER run docker commands directly on the primary host
- Always use incus exec cr-server-new -- for Docker ops
- Warn before any restart/stop of production services
- Always test with SELECT before UPDATE/DELETE
`.trim();
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Dangerous patterns
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const DANGEROUS: { pattern: RegExp; reason: string }[] = [
{ pattern: /rm\s+-[rRf]{1,3}\s+[\/~]/, reason: "Deleting from root or home!" },
{ pattern: /rm\s+-rf/, reason: "Recursive force delete" },
{ pattern: /DROP\s+(TABLE|DATABASE|SCHEMA)/i, reason: "SQL DROP detected" },
{ pattern: /TRUNCATE\s+TABLE/i, reason: "SQL TRUNCATE detected" },
{ pattern: /DELETE\s+FROM\s+\w+\s*(WHERE\s+1=1\s*)?(;|$)/i, reason: "Potentially unsafe DELETE" },
{ pattern: /UPDATE\s+\w+\s+SET\s+.*\s*;?\s*$/i, reason: "UPDATE without WHERE β may affect all rows" },
{ pattern: /docker\s+system\s+prune/, reason: "Docker prune β removes all unused data" },
{ pattern: /incus\s+(delete|stop)\s+\S+/, reason: "Incus container operation" },
{ pattern: /systemctl\s+(stop|disable|restart)\s+(haproxy|docker|nginx)/, reason: "Touching critical production service" },
{ pattern: /git\s+push\s+.*--force/, reason: "Force push β may overwrite remote history" },
{ pattern: /git\s+reset\s+--hard/, reason: "Hard reset β may lose uncommitted changes" },
{ pattern: /mkfs\./, reason: "Filesystem format command!" },
{ pattern: /dd\s+if=/, reason: "Raw disk copy via dd" },
{ pattern: /curl.*\|\s*(ba)?sh/, reason: "Piping remote script to shell" },
{ pattern: /wget.*\|\s*(ba)?sh/, reason: "Piping remote script to shell" },
{ pattern: /chmod\s+-R\s+777/, reason: "Recursive world-writable permissions" },
];
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Helpers
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function runSSH(cmd: string, timeout = 15000): string {
try {
return execSync(`${SSH} "${cmd.replace(/"/g, '\\"')}"`, { timeout, encoding: "utf8" }).trim();
} catch (e: any) {
return e?.stdout?.trim() || e?.message || "SSH error";
}
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Extension
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export default function (pi: ExtensionAPI) {
let focus: string | undefined;
let activeCtx: ExtensionContext | undefined;
const toolCounts: Record = {};
// ββ Tool tracking βββββββββββββββββββββββββββββββββββββββββββββββββββββ
pi.on("tool_execution_end", async (event) => {
toolCounts[event.toolName] = (toolCounts[event.toolName] || 0) + 1;
});
// ββ Session start βββββββββββββββββββββββββββββββββββββββββββββββββββββ
pi.on("session_start", async (_event, ctx) => {
applyExtensionDefaults(import.meta.url, ctx);
activeCtx = ctx;
// Ask for focus task (non-blocking)
void (async () => {
const answer = await ctx.ui.input(
"π― What are you working on today?",
"e.g. Debug launchgood sync, check cr-server disk, investigate slow queries"
);
if (answer?.trim()) {
focus = answer.trim();
setFocusWidget(ctx);
ctx.ui.notify(`π― Focus set: ${focus}`, "success");
} else {
ctx.ui.notify("π‘ No focus set. Use /focus to set one anytime.", "info");
}
})();
// ββ Footer ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ctx.ui.setFooter((tui, theme, footerData) => {
const unsub = footerData.onBranchChange(() => tui.requestRender());
return {
dispose: unsub,
invalidate() {},
render(width: number): string[] {
// Accumulate tokens + cost
let tokIn = 0, tokOut = 0, cost = 0;
for (const entry of ctx.sessionManager.getBranch()) {
if (entry.type === "message" && entry.message.role === "assistant") {
const m = entry.message as AssistantMessage;
tokIn += m.usage.input;
tokOut += m.usage.output;
cost += m.usage.cost.total;
}
}
const fmt = (n: number) => n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`;
const model = ctx.model?.id || "no-model";
const branch = footerData.getGitBranch();
const dir = basename(ctx.cwd);
// Context bar
const usage = ctx.getContextUsage();
const pct = usage?.percent ?? 0;
const filled = Math.round(pct / 10);
const bar =
(pct < 70 ? theme.fg("success", "β".repeat(filled)) : pct < 90 ? theme.fg("warning", "β".repeat(filled)) : theme.fg("error", "β".repeat(filled))) +
theme.fg("dim", "β".repeat(10 - filled));
// Line 1: model | context | tokens | cost
const l1Left =
theme.fg("accent", " Pi") +
theme.fg("dim", ` ${model} `) +
bar +
theme.fg("dim", ` ${Math.round(pct)}%`);
const l1Right =
theme.fg("success", fmt(tokIn)) +
theme.fg("dim", "β ") +
theme.fg("accent", fmt(tokOut)) +
theme.fg("dim", "β ") +
theme.fg("warning", `$${cost.toFixed(4)}`) +
theme.fg("dim", " ");
const pad1 = " ".repeat(Math.max(1, width - visibleWidth(l1Left) - visibleWidth(l1Right)));
const line1 = truncateToWidth(l1Left + pad1 + l1Right, width, "");
// Line 2: cwd | branch | tools
const l2Left =
theme.fg("dim", ` ${dir}`) +
(branch
? theme.fg("dim", " ") + theme.fg("warning", "(") + theme.fg("success", branch) + theme.fg("warning", ")")
: "") +
theme.fg("dim", " βΊ 159.195.60.33");
const entries = Object.entries(toolCounts);
const l2Right = entries.length === 0
? theme.fg("dim", "no tools yet ")
: entries.map(([n, c]) => theme.fg("accent", n) + theme.fg("dim", ":") + theme.fg("success", `${c}`)).join(theme.fg("dim", " ")) + " ";
const pad2 = " ".repeat(Math.max(1, width - visibleWidth(l2Left) - visibleWidth(l2Right)));
const line2 = truncateToWidth(l2Left + pad2 + l2Right, width, "");
return [line1, line2];
},
};
});
});
pi.on("session_switch", async (_event, ctx) => { activeCtx = ctx; });
// ββ Safety guard ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pi.on("tool_call", async (event, ctx) => {
if (isToolCallEventType("bash", event)) {
const cmd = event.input.command;
for (const { pattern, reason } of DANGEROUS) {
if (pattern.test(cmd)) {
const confirmed = await ctx.ui.confirm(
"β οΈ Destructive Operation",
`${reason}\n\nCommand:\n${cmd}\n\nRun this on LIVE production?`,
{ timeout: 30000 }
);
if (!confirmed) {
ctx.ui.notify(`π‘οΈ Blocked: ${reason}`, "warning");
ctx.abort();
return {
block: true,
reason: `π BLOCKED: ${reason}\n\nUser declined. Ask what to do instead.`,
};
}
break;
}
}
}
return { block: false };
});
// ββ System prompt injection βββββββββββββββββββββββββββββββββββββββββββ
pi.on("before_agent_start", async (event) => {
let extra = `\n\n${INFRA_CONTEXT}`;
if (focus) {
extra += `\n\n\n${focus}\nStay focused on this task. If asked something unrelated, note it but bring back to the focus.\n`;
}
return { systemPrompt: event.systemPrompt + extra };
});
// ββ Focus widget ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function setFocusWidget(ctx: ExtensionContext) {
ctx.ui.setWidget("focus", () => ({
render(width: number): string[] {
const line = truncateToWidth(` π― ${focus}`, width - 2, "β¦");
return [
" ".repeat(width),
line + " ".repeat(Math.max(0, width - visibleWidth(line))),
" ".repeat(width),
];
},
invalidate() {},
}));
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Commands
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pi.registerCommand("stop", {
description: "Abort current AI action",
handler: async (args, ctx) => {
ctx.abort();
ctx.ui.notify(args?.trim() ? `π Stopped: ${args.trim()}` : "π Stopped.", "warning");
},
});
pi.registerCommand("focus", {
description: "Set or change current task focus",
handler: async (args, ctx) => {
if (args?.trim()) {
focus = args.trim();
setFocusWidget(ctx);
ctx.ui.notify(`π― Focus set: ${focus}`, "success");
} else {
const answer = await ctx.ui.input("π― What are you working on?", focus || "e.g. Debug sync, check logs...");
if (answer?.trim()) {
focus = answer.trim();
setFocusWidget(ctx);
ctx.ui.notify(`π― Focus set: ${focus}`, "success");
}
}
},
});
pi.registerCommand("infra", {
description: "Show infra topology",
handler: async (_args, ctx) => {
ctx.ui.notify(
"πΊοΈ Infra Topology\n\n" +
"Server: root@159.195.60.33\n\n" +
"Containers:\n" +
" cr-server-new (10.213.16.224) β CharityRight + Docker\n" +
" qc-server-new (10.213.16.234) β QuikCue\n" +
" qc-server β STOPPED\n\n" +
"HAProxy:\n" +
" charityright.* β cr-server-new:443\n" +
" quikcue.* β qc-server-new:443\n" +
" antivirus.quikcue.com β localhost:8877\n\n" +
"Services (on cr-server-new):\n" +
" /opt/ayn-antivirus\n" +
" /opt/enthuse-db-sync-v2\n" +
" /opt/launchgood-sync\n" +
" /root/legacy-donation-system-laravel\n" +
" /root/redis-v2",
"info"
);
},
});
pi.registerCommand("health", {
description: "Live check of containers and key services",
handler: async (_args, ctx) => {
ctx.ui.notify("β³ Checking infra health...", "info");
try {
const containers = runSSH("incus list --format=csv -c ns", 10000);
const haproxy = runSSH("systemctl is-active haproxy", 5000);
const docker = runSSH("incus exec cr-server-new -- docker ps --format '{{.Names}}\\t{{.Status}}' 2>/dev/null | head -10", 10000);
const disk = runSSH("df -h / | tail -1 | awk '{print $5\" used of \"$2}'", 5000);
const mem = runSSH("free -m | awk 'NR==2{printf \"%sMB / %sMB (%.0f%%)\", $3,$2,$3*100/$2}'", 5000);
ctx.ui.notify(
"β
Infrastructure Health\n\n" +
`HAProxy: ${haproxy === "active" ? "β
active" : "β " + haproxy}\n\n` +
`Containers:\n${containers.split("\n").map(l => " " + l).join("\n")}\n\n` +
`Docker (cr-server-new):\n${docker.split("\n").map(l => " " + l).join("\n")}\n\n` +
`Disk: ${disk}\n` +
`RAM: ${mem}`,
"info"
);
} catch (e: any) {
ctx.ui.notify(`β Health check failed: ${e.message}`, "error");
}
},
});
pi.registerCommand("logs", {
description: "Tail logs for a service (usage: /logs launchgood)",
handler: async (args, ctx) => {
const services: Record = {
launchgood: "incus exec cr-server-new -- journalctl -u launchgood-sync -n 50 --no-pager 2>/dev/null || incus exec cr-server-new -- tail -50 /opt/launchgood-sync/logs/app.log 2>/dev/null",
enthuse: "incus exec cr-server-new -- journalctl -u enthuse-sync -n 50 --no-pager 2>/dev/null || incus exec cr-server-new -- tail -50 /opt/enthuse-db-sync-v2/logs/app.log 2>/dev/null",
antivirus: "journalctl -u ayn-antivirus -n 50 --no-pager 2>/dev/null",
haproxy: "journalctl -u haproxy -n 50 --no-pager",
laravel: "incus exec cr-server-new -- tail -50 /root/legacy-donation-system-laravel/storage/logs/laravel.log 2>/dev/null",
};
const svc = args?.trim().toLowerCase();
if (!svc || !services[svc]) {
const opts = Object.keys(services);
const choice = await ctx.ui.select("Which service logs?", opts);
if (!choice) return;
const cmd = services[choice];
ctx.ui.notify("β³ Fetching logs...", "info");
const out = runSSH(cmd, 15000);
ctx.ui.notify(`π ${choice} logs:\n\n${out.slice(-3000)}`, "info");
} else {
ctx.ui.notify("β³ Fetching logs...", "info");
const out = runSSH(services[svc], 15000);
ctx.ui.notify(`π ${svc} logs:\n\n${out.slice(-3000)}`, "info");
}
},
});
pi.registerCommand("ssh", {
description: "Run a raw SSH command on the server",
handler: async (args, ctx) => {
if (!args?.trim()) {
ctx.ui.notify("Usage: /ssh \nExample: /ssh incus list", "warning");
return;
}
ctx.ui.notify(`β³ Running: ${args.trim()}`, "info");
const out = runSSH(args.trim(), 20000);
ctx.ui.notify(`$ ${args.trim()}\n\n${out}`, "info");
},
});
}