mortgram-cli 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/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const spawner_1 = require("./lib/spawner");
9
+ const config_1 = require("./lib/config");
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ const path_1 = __importDefault(require("path"));
12
+ // Load environment variables from the parent directory if possible, or current
13
+ dotenv_1.default.config({ path: path_1.default.join(process.cwd(), "..", ".env.local") });
14
+ dotenv_1.default.config(); // Fallback to current dir
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name("mortgram")
18
+ .description("MORTGRAM Observer CLI - Zero-code observability wrapper")
19
+ .version("1.0.0");
20
+ program
21
+ .command("login <key>")
22
+ .description("Authenticate your terminal with your MORTGRAM API Key")
23
+ .action((key) => {
24
+ (0, config_1.saveConfig)(key);
25
+ console.log("MORTGRAM: Authentication successful. Key saved to ~/.mortgramrc");
26
+ });
27
+ program
28
+ .command("run <command...>")
29
+ .description("Run an agent command with MORTGRAM observability")
30
+ .action(async (commandParts) => {
31
+ const fullCommand = commandParts.join(" ");
32
+ console.log(`MORTGRAM: Wrapping command: "${fullCommand}"`);
33
+ await (0, spawner_1.spawnAgent)(fullCommand);
34
+ });
35
+ program.parse();
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.saveConfig = saveConfig;
7
+ exports.loadConfig = loadConfig;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".mortgramrc");
12
+ function saveConfig(key) {
13
+ const config = { api_key: key };
14
+ fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
15
+ }
16
+ function loadConfig() {
17
+ if (fs_1.default.existsSync(CONFIG_FILE)) {
18
+ try {
19
+ const content = fs_1.default.readFileSync(CONFIG_FILE, "utf-8");
20
+ const config = JSON.parse(content);
21
+ return config.api_key || null;
22
+ }
23
+ catch (e) {
24
+ return null;
25
+ }
26
+ }
27
+ return null;
28
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.trackUsage = trackUsage;
7
+ exports.checkBudget = checkBudget;
8
+ exports.getCurrentSpend = getCurrentSpend;
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const MODEL_COSTS = {
11
+ "gpt-4o": 0.03, // per 1k input/output avg
12
+ "gpt-4-turbo": 0.02,
13
+ "claude-3-opus": 0.045
14
+ };
15
+ let currentSpend = 0.0;
16
+ const SESSION_BUDGET = parseFloat(process.env.MG_SESSION_BUDGET || "1.00"); // default $1
17
+ function trackUsage(logLine) {
18
+ // Regex for: "Using model: gpt-4o" or "Token usage: 500"
19
+ // This is a naive implementation for demo purposes
20
+ // Check for model usage
21
+ for (const [model, cost] of Object.entries(MODEL_COSTS)) {
22
+ if (logLine.includes(model)) {
23
+ // Assume an average specific cost per interaction for now
24
+ // In a real app, we'd parse exact token counts
25
+ const estimatedCost = 0.002; // $0.002 per interaction
26
+ currentSpend += estimatedCost;
27
+ console.log(chalk_1.default.yellow(`MORTGRAM: +$${estimatedCost.toFixed(4)} (Spend: $${currentSpend.toFixed(4)})`));
28
+ }
29
+ }
30
+ }
31
+ function checkBudget(childProcess) {
32
+ if (currentSpend > SESSION_BUDGET) {
33
+ console.log(chalk_1.default.red.bold(`\n!!! MORTGRAM FINANCIAL GUARDRAIL !!!`));
34
+ console.log(chalk_1.default.red(`Session budget of $${SESSION_BUDGET} exceeded.`));
35
+ console.log(chalk_1.default.red(`Terminating process immediately.`));
36
+ childProcess.kill('SIGKILL');
37
+ process.exit(1);
38
+ }
39
+ }
40
+ function getCurrentSpend() {
41
+ return currentSpend;
42
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ingestLog = ingestLog;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const dotenv_1 = __importDefault(require("dotenv"));
9
+ const config_1 = require("./config");
10
+ // ensure env is loaded
11
+ dotenv_1.default.config();
12
+ const API_KEY = process.env.MG_LIVE_KEY || (0, config_1.loadConfig)();
13
+ const API_URL = process.env.MORTGRAM_API_URL || "http://localhost:3000/api/ingest";
14
+ const AGENT_ID = process.env.MG_AGENT_ID || "cli-agent";
15
+ async function ingestLog(type, content, metadata = {}) {
16
+ if (!API_KEY)
17
+ return;
18
+ try {
19
+ await axios_1.default.post(API_URL, {
20
+ agent_id: AGENT_ID,
21
+ type,
22
+ content,
23
+ metadata
24
+ }, {
25
+ headers: {
26
+ "Authorization": `Bearer ${API_KEY}`,
27
+ "Content-Type": "application/json"
28
+ }
29
+ });
30
+ }
31
+ catch (error) {
32
+ // Silently fail to not disrupt the agent
33
+ // console.error("MORTGRAM: Ingest failed", error.message);
34
+ }
35
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startNeuralLink = startNeuralLink;
7
+ const ably_1 = __importDefault(require("ably"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ dotenv_1.default.config();
11
+ const ABLY_KEY = process.env.ABLY_API_KEY;
12
+ const AGENT_ID = process.env.MG_AGENT_ID || "cli-agent";
13
+ let client = null;
14
+ async function startNeuralLink(childProcess) {
15
+ if (!ABLY_KEY) {
16
+ console.warn(chalk_1.default.yellow("MORTGRAM: No ABLY_API_KEY found. Neural link disabled."));
17
+ return { close: () => { } };
18
+ }
19
+ client = new ably_1.default.Realtime(ABLY_KEY);
20
+ const channel = client.channels.get(`control:${AGENT_ID}`);
21
+ await channel.subscribe("KILL", (message) => {
22
+ console.log(chalk_1.default.bgRed.white.bold("\n!!! MORTGRAM NEURAL LINK: KILL SIGNAL RECEIVED !!!\n"));
23
+ childProcess.kill('SIGKILL');
24
+ process.exit(0);
25
+ });
26
+ console.log(chalk_1.default.blue(`MORTGRAM: Neural Link active on channel control:${AGENT_ID}`));
27
+ return {
28
+ close: () => {
29
+ if (client)
30
+ client.close();
31
+ }
32
+ };
33
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.spawnAgent = spawnAgent;
7
+ const child_process_1 = require("child_process");
8
+ const logger_1 = require("./logger");
9
+ const guard_1 = require("./guard");
10
+ const vitals_1 = require("./vitals");
11
+ const neural_1 = require("./neural");
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ let childProcess = null;
14
+ async function spawnAgent(command) {
15
+ console.log(chalk_1.default.green("MORTGRAM: Initializing Observer..."));
16
+ // Parse command string into cmd and args
17
+ // crude parsing, careful with quotes
18
+ const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
19
+ if (parts.length === 0)
20
+ return;
21
+ const cmd = parts[0];
22
+ if (!cmd)
23
+ return;
24
+ const args = parts.slice(1).map(arg => arg.replace(/^"|"$/g, ''));
25
+ childProcess = (0, child_process_1.spawn)(cmd, args, {
26
+ stdio: ['inherit', 'pipe', 'pipe'],
27
+ shell: true,
28
+ env: { ...process.env, MORTGRAM_WRAPPED: "true" }
29
+ });
30
+ // Neural Link (Kill Switch)
31
+ const neural = await (0, neural_1.startNeuralLink)(childProcess);
32
+ // Vitals Monitor
33
+ const vitalsInterval = (0, vitals_1.startVitals)(childProcess);
34
+ // Stdout Interception
35
+ childProcess.stdout.on('data', (data) => {
36
+ const line = data.toString();
37
+ process.stdout.write(line); // Pass through
38
+ // Analyze
39
+ analyzeLog(line);
40
+ });
41
+ // Stderr Interception
42
+ childProcess.stderr.on('data', (data) => {
43
+ const line = data.toString();
44
+ process.stderr.write(line); // Pass through
45
+ analyzeLog(line, true);
46
+ });
47
+ childProcess.on('close', (code) => {
48
+ console.log(chalk_1.default.gray(`MORTGRAM: Child process exited with code ${code}`));
49
+ clearInterval(vitalsInterval);
50
+ neural.close();
51
+ process.exit(code);
52
+ });
53
+ }
54
+ function analyzeLog(content, isError = false) {
55
+ // 1. Ingest
56
+ if (content.includes("Thought:") || content.includes("Action:") || content.includes("Error:")) {
57
+ let type = "log";
58
+ if (content.includes("Thought:"))
59
+ type = "thought";
60
+ if (content.includes("Action:"))
61
+ type = "action";
62
+ if (isError)
63
+ type = "error";
64
+ (0, logger_1.ingestLog)(type, content.trim());
65
+ }
66
+ // 2. Financial Guard
67
+ // Detect model usage, e.g. "Using gpt-4o"
68
+ (0, guard_1.trackUsage)(content);
69
+ (0, guard_1.checkBudget)(childProcess);
70
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startVitals = startVitals;
7
+ const pidusage_1 = __importDefault(require("pidusage"));
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const guard_1 = require("./guard");
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ const config_1 = require("./config");
12
+ dotenv_1.default.config();
13
+ const API_KEY = process.env.MG_LIVE_KEY || (0, config_1.loadConfig)();
14
+ const API_URL = process.env.MORTGRAM_API_URL ? `${process.env.MORTGRAM_API_URL}/heartbeat` : "http://localhost:3000/api/cron/heartbeat";
15
+ // Note: reusing cron heartbeat or creating a specific agent heartbeat endpoint?
16
+ // For CLI, we likely want a direct ingestion endpoint or use the log ingestion with type "heartbeat"
17
+ const INGEST_URL = process.env.MORTGRAM_API_URL || "http://localhost:3000/api/ingest";
18
+ const AGENT_ID = process.env.MG_AGENT_ID || "cli-agent";
19
+ function startVitals(childProcess) {
20
+ const interval = setInterval(async () => {
21
+ if (!childProcess || childProcess.killed)
22
+ return;
23
+ try {
24
+ const stats = await (0, pidusage_1.default)(childProcess.pid);
25
+ const spend = (0, guard_1.getCurrentSpend)();
26
+ // Send heartbeat log
27
+ if (API_KEY) {
28
+ await axios_1.default.post(INGEST_URL, {
29
+ agent_id: AGENT_ID,
30
+ type: "status", // Special type for heartbeat/status
31
+ content: `Heartbeat: CPU ${stats.cpu.toFixed(1)}% | MEM ${(stats.memory / 1024 / 1024).toFixed(0)}MB | $${spend.toFixed(4)}`,
32
+ metadata: {
33
+ cpu: stats.cpu,
34
+ memory: stats.memory,
35
+ spend: spend,
36
+ status: "online"
37
+ }
38
+ }, {
39
+ headers: { "Authorization": `Bearer ${API_KEY}` }
40
+ });
41
+ }
42
+ }
43
+ catch (err) {
44
+ // pidusage might fail if process dies quickly
45
+ }
46
+ }, 30000); // 30s
47
+ return interval;
48
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "mortgram-cli",
3
+ "version": "1.0.0",
4
+ "description": "Zero-code observability wrapper for AI agents",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "prepublishOnly": "npm run build"
9
+ },
10
+ "keywords": [
11
+ "ai",
12
+ "observability",
13
+ "agent",
14
+ "cli"
15
+ ],
16
+ "author": "MORTGRAM",
17
+ "license": "ISC",
18
+ "dependencies": {
19
+ "ably": "^2.17.1",
20
+ "axios": "^1.13.5",
21
+ "chalk": "^4.1.2",
22
+ "commander": "^14.0.3",
23
+ "dotenv": "^17.2.4",
24
+ "pidusage": "^4.0.1",
25
+ "strip-ansi": "^6.0.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.2.2",
29
+ "@types/pidusage": "^2.0.5",
30
+ "typescript": "^5.9.3"
31
+ },
32
+ "bin": {
33
+ "mortgram": "./dist/index.js"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md"
38
+ ]
39
+ }