mortgram-cli 1.0.1 → 1.0.2

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 CHANGED
@@ -9,6 +9,8 @@ const spawner_1 = require("./lib/spawner");
9
9
  const config_1 = require("./lib/config");
10
10
  const dotenv_1 = __importDefault(require("dotenv"));
11
11
  const path_1 = __importDefault(require("path"));
12
+ const axios_1 = __importDefault(require("axios"));
13
+ const chalk_1 = __importDefault(require("chalk"));
12
14
  // Load environment variables from the parent directory if possible, or current
13
15
  dotenv_1.default.config({ path: path_1.default.join(process.cwd(), "..", ".env.local"), quiet: true });
14
16
  dotenv_1.default.config({ quiet: true }); // Fallback to current dir
@@ -16,20 +18,53 @@ const program = new commander_1.Command();
16
18
  program
17
19
  .name("mortgram")
18
20
  .description("MORTGRAM Observer CLI - Zero-code observability wrapper")
19
- .version("1.0.0");
21
+ .version("1.0.1");
20
22
  program
21
23
  .command("login <key>")
22
24
  .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");
25
+ .action(async (key) => {
26
+ try {
27
+ const apiUrl = process.env.MORTGRAM_API_URL || "http://localhost:3000/api";
28
+ console.log(chalk_1.default.yellow("Authenticating with MORTGRAM HQ..."));
29
+ const res = await axios_1.default.get(`${apiUrl}/profile`, {
30
+ headers: { "Authorization": `Bearer ${key}` }
31
+ });
32
+ if (res.data.profile) {
33
+ const ablyKey = res.data.profile.ably_key;
34
+ (0, config_1.saveConfig)(key, ablyKey);
35
+ console.log(chalk_1.default.green("✔ Authentication successful."));
36
+ if (ablyKey) {
37
+ console.log(chalk_1.default.green("✔ Neural Link established (Ably Key retrieved)."));
38
+ }
39
+ else {
40
+ console.log(chalk_1.default.yellow("⚠ Neural Link warning: No Ably Key returned from server."));
41
+ }
42
+ console.log(chalk_1.default.gray("Configuration saved to ~/.mortgramrc"));
43
+ }
44
+ else {
45
+ console.error(chalk_1.default.red("Authentication failed: Invalid response from server."));
46
+ }
47
+ }
48
+ catch (error) {
49
+ console.error(chalk_1.default.red(`Authentication failed: ${error.message}`));
50
+ if (error.response?.status === 401) {
51
+ console.error(chalk_1.default.red("Error: Invalid API Key."));
52
+ }
53
+ }
26
54
  });
27
55
  program
28
56
  .command("run <command...>")
29
57
  .description("Run an agent command with MORTGRAM observability")
30
58
  .action(async (commandParts) => {
31
59
  const fullCommand = commandParts.join(" ");
32
- console.log(`MORTGRAM: Wrapping command: "${fullCommand}"`);
33
- await (0, spawner_1.spawnAgent)(fullCommand);
60
+ console.log(chalk_1.default.blue(`MORTGRAM: Wrapping command: "${fullCommand}"`));
61
+ console.log(chalk_1.default.blue(`MORTGRAM: Listening for uplink...`));
62
+ try {
63
+ await (0, spawner_1.spawnAgent)(fullCommand);
64
+ }
65
+ catch (e) {
66
+ console.error(chalk_1.default.red(`MORTGRAM: Failed to launch agent: ${e.message}`));
67
+ process.exit(1);
68
+ }
34
69
  });
35
70
  program.parse();
@@ -9,8 +9,8 @@ const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const os_1 = __importDefault(require("os"));
11
11
  const CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".mortgramrc");
12
- function saveConfig(key) {
13
- const config = { api_key: key };
12
+ function saveConfig(apiKey, ablyKey) {
13
+ const config = { api_key: apiKey, ably_key: ablyKey };
14
14
  fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
15
15
  }
16
16
  function loadConfig() {
@@ -18,11 +18,14 @@ function loadConfig() {
18
18
  try {
19
19
  const content = fs_1.default.readFileSync(CONFIG_FILE, "utf-8");
20
20
  const config = JSON.parse(content);
21
- return config.api_key || null;
21
+ return {
22
+ api_key: config.api_key || null,
23
+ ably_key: config.ably_key || null
24
+ };
22
25
  }
23
26
  catch (e) {
24
- return null;
27
+ return { api_key: null, ably_key: null };
25
28
  }
26
29
  }
27
- return null;
30
+ return { api_key: null, ably_key: null };
28
31
  }
@@ -9,7 +9,8 @@ const dotenv_1 = __importDefault(require("dotenv"));
9
9
  const config_1 = require("./config");
10
10
  // ensure env is loaded
11
11
  dotenv_1.default.config({ quiet: true });
12
- const API_KEY = process.env.MG_LIVE_KEY || (0, config_1.loadConfig)();
12
+ const config = (0, config_1.loadConfig)();
13
+ const API_KEY = process.env.MG_LIVE_KEY || config.api_key;
13
14
  const API_URL = process.env.MORTGRAM_API_URL || "http://localhost:3000/api/ingest";
14
15
  const AGENT_ID = process.env.MG_AGENT_ID || "cli-agent";
15
16
  async function ingestLog(type, content, metadata = {}) {
@@ -5,29 +5,39 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.startNeuralLink = startNeuralLink;
7
7
  const ably_1 = __importDefault(require("ably"));
8
+ const logger_1 = require("./logger");
8
9
  const chalk_1 = __importDefault(require("chalk"));
9
10
  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;
11
+ const config_1 = require("./config");
12
+ dotenv_1.default.config({ quiet: true });
14
13
  async function startNeuralLink(childProcess) {
15
- if (!ABLY_KEY) {
16
- console.warn(chalk_1.default.yellow("MORTGRAM: No ABLY_API_KEY found. Neural link disabled."));
14
+ const config = (0, config_1.loadConfig)();
15
+ const API_KEY = process.env.ABLY_API_KEY || config.ably_key;
16
+ const AGENT_ID = process.env.MG_AGENT_ID || "cli-agent"; // In real usage, this might be dynamic or passed as arg
17
+ if (!API_KEY) {
18
+ console.log(chalk_1.default.yellow("MORTGRAM: No ABLY_API_KEY found. Neural link disabled."));
17
19
  return { close: () => { } };
18
20
  }
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');
21
+ const realtime = new ably_1.default.Realtime(API_KEY);
22
+ // Connect to specific agent control channel
23
+ const channel = realtime.channels.get(`control:${AGENT_ID}`);
24
+ await channel.subscribe("kill", async (message) => {
25
+ console.log(chalk_1.default.red.bold("\n⚡ MORTGRAM: KILL SIGNAL RECEIVED ⚡"));
26
+ console.log(chalk_1.default.red(`Reason: ${message.data.reason || "Manual Termination"}`));
27
+ // Log the event before dying
28
+ await (0, logger_1.ingestLog)("action", "TERMINATED via Neural Link", {
29
+ status: "killed",
30
+ reason: message.data.reason
31
+ });
32
+ // Kill the child process
33
+ childProcess.kill("SIGKILL");
34
+ // Close Ably connection
35
+ realtime.close();
24
36
  process.exit(0);
25
37
  });
26
- console.log(chalk_1.default.blue(`MORTGRAM: Neural Link active on channel control:${AGENT_ID}`));
38
+ // Also subscribe to PAUSE signals if we implement them later
39
+ // await channel.subscribe("pause", ...);
27
40
  return {
28
- close: () => {
29
- if (client)
30
- client.close();
31
- }
41
+ close: () => realtime.close()
32
42
  };
33
43
  }
@@ -13,8 +13,9 @@ const chalk_1 = __importDefault(require("chalk"));
13
13
  let childProcess = null;
14
14
  async function spawnAgent(command) {
15
15
  console.log(chalk_1.default.green("MORTGRAM: Initializing Observer..."));
16
+ // Immediate handshake
17
+ await (0, logger_1.ingestLog)("log", "Observer Linked. Awaiting agent output.", { status: "online" });
16
18
  // Parse command string into cmd and args
17
- // crude parsing, careful with quotes
18
19
  const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
19
20
  if (parts.length === 0)
20
21
  return;
@@ -44,8 +45,10 @@ async function spawnAgent(command) {
44
45
  process.stderr.write(line); // Pass through
45
46
  analyzeLog(line, true);
46
47
  });
47
- childProcess.on('close', (code) => {
48
+ childProcess.on('close', async (code) => {
48
49
  console.log(chalk_1.default.gray(`MORTGRAM: Child process exited with code ${code}`));
50
+ // Final log
51
+ await (0, logger_1.ingestLog)("log", `Agent process terminated (code ${code})`, { status: "offline" });
49
52
  clearInterval(vitalsInterval);
50
53
  neural.close();
51
54
  process.exit(code);
@@ -10,7 +10,8 @@ const guard_1 = require("./guard");
10
10
  const dotenv_1 = __importDefault(require("dotenv"));
11
11
  const config_1 = require("./config");
12
12
  dotenv_1.default.config({ quiet: true });
13
- const API_KEY = process.env.MG_LIVE_KEY || (0, config_1.loadConfig)();
13
+ const config = (0, config_1.loadConfig)();
14
+ const API_KEY = process.env.MG_LIVE_KEY || config.api_key;
14
15
  const API_URL = process.env.MORTGRAM_API_URL ? `${process.env.MORTGRAM_API_URL}/heartbeat` : "http://localhost:3000/api/cron/heartbeat";
15
16
  // Note: reusing cron heartbeat or creating a specific agent heartbeat endpoint?
16
17
  // For CLI, we likely want a direct ingestion endpoint or use the log ingestion with type "heartbeat"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mortgram-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Zero-code observability wrapper for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {