monty-cli 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/monty.js +59 -1
  2. package/package.json +1 -1
package/monty.js CHANGED
@@ -4,7 +4,7 @@
4
4
  const fs = require("node:fs");
5
5
  const os = require("node:os");
6
6
  const path = require("node:path");
7
- const { spawnSync } = require("node:child_process");
7
+ const { spawnSync, execSync } = require("node:child_process");
8
8
 
9
9
  const home = os.homedir();
10
10
  const montyDir = path.join(home, ".monty");
@@ -22,6 +22,7 @@ async function main() {
22
22
  if (command === "hook") return hookCommandHandler(args);
23
23
  if (command === "run") return runWrapped(args);
24
24
  if (command === "sync") return syncHistory(args);
25
+ if (command === "daemon") return daemon(args);
25
26
  if (command === "doctor") return doctor();
26
27
  if (command === "help" || command === "--help" || command === "-h") return help();
27
28
 
@@ -286,6 +287,62 @@ async function sendHeartbeat() {
286
287
  }
287
288
  }
288
289
 
290
+ function detectLidClosed() {
291
+ try {
292
+ const out = execSync("ioreg -r -k AppleClamshellState -d 4 | grep AppleClamshellState | head -1", { encoding: "utf8", timeout: 3000 });
293
+ if (out.includes("Yes")) return true;
294
+ if (out.includes("No")) return false;
295
+ return false;
296
+ } catch {
297
+ return false;
298
+ }
299
+ }
300
+
301
+ function detectSessions() {
302
+ let claude = 0;
303
+ let codex = 0;
304
+ try { claude = parseInt(execSync("pgrep -x claude 2>/dev/null | wc -l", { encoding: "utf8", timeout: 2000 }).trim(), 10) || 0; } catch {}
305
+ try { codex = parseInt(execSync("pgrep -x codex 2>/dev/null | wc -l", { encoding: "utf8", timeout: 2000 }).trim(), 10) || 0; } catch {}
306
+ return { claude, codex };
307
+ }
308
+
309
+ async function sendPresence() {
310
+ const config = readConfig();
311
+ const siteUrl = cleanUrl(config.siteUrl || process.env.MONTY_SITE_URL || "https://www.trymonty.ai");
312
+ const headers = { "content-type": "application/json" };
313
+ const token = config.ingestToken || process.env.MONTY_INGEST_TOKEN;
314
+ if (token) headers.authorization = `Bearer ${token}`;
315
+
316
+ const controller = new AbortController();
317
+ const timeout = setTimeout(() => controller.abort(), 5000);
318
+ try {
319
+ await fetch(`${siteUrl}/api/lid-state`, {
320
+ method: "POST",
321
+ headers,
322
+ body: JSON.stringify({
323
+ user_name: config.userName || process.env.MONTY_USER || process.env.USER || "unknown",
324
+ lid_closed: detectLidClosed(),
325
+ sessions: detectSessions(),
326
+ }),
327
+ signal: controller.signal,
328
+ });
329
+ } catch {}
330
+ finally { clearTimeout(timeout); }
331
+ }
332
+
333
+ async function daemon(args) {
334
+ const options = parseArgs(args);
335
+ const intervalSec = Number(options.interval) || 5;
336
+ console.log(`Monty daemon running (every ${intervalSec}s). Press Ctrl+C to stop.`);
337
+
338
+ const tick = async () => {
339
+ await sendPresence().catch(() => {});
340
+ };
341
+
342
+ await tick();
343
+ setInterval(tick, intervalSec * 1000);
344
+ }
345
+
289
346
  async function runWrapped(args) {
290
347
  const [tool, ...toolArgs] = args;
291
348
  if (!tool || !["claude", "codex"].includes(tool)) {
@@ -964,6 +1021,7 @@ function help() {
964
1021
  Usage:
965
1022
  monty install --site http://localhost:3000 --team default
966
1023
  monty sync Sync all Claude Code & Codex history with real token counts
1024
+ monty daemon Run background presence reporter (lid state + session count)
967
1025
  monty capture --source test --prompt "hello"
968
1026
  monty run codex exec "prompt"
969
1027
  monty run claude -p "prompt"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monty-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Live AI prompt feed and token leaderboard for your engineering team. Works with Claude Code and Codex CLI.",
5
5
  "license": "MIT",
6
6
  "bin": {