jefrichat-mcp 0.16.0 → 0.18.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/http.js +24 -5
- package/dist/index.js +58 -15
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -49466,6 +49466,7 @@ function setPrefs(patch) {
|
|
|
49466
49466
|
}
|
|
49467
49467
|
return { ...prefs };
|
|
49468
49468
|
}
|
|
49469
|
+
var LOG_FILE = np.join(os2.homedir(), ".jefri", "notify.log");
|
|
49469
49470
|
|
|
49470
49471
|
// src/autonomous.ts
|
|
49471
49472
|
import fs3 from "node:fs";
|
|
@@ -49479,13 +49480,14 @@ var DEFAULTS2 = {
|
|
|
49479
49480
|
replyMode: "mentions",
|
|
49480
49481
|
persona: "",
|
|
49481
49482
|
replyToBots: false,
|
|
49482
|
-
ownerOnly:
|
|
49483
|
+
ownerOnly: true,
|
|
49484
|
+
// SECURITY: default to only the owner can direct an autonomous agent
|
|
49483
49485
|
contextMessages: 12,
|
|
49484
49486
|
promptTemplate: DEFAULT_TEMPLATE
|
|
49485
49487
|
};
|
|
49486
49488
|
var DIR = np2.join(os3.homedir(), ".jefri");
|
|
49487
49489
|
var FILE = np2.join(DIR, "autonomous.json");
|
|
49488
|
-
var
|
|
49490
|
+
var LOG_FILE2 = np2.join(DIR, "autonomous.log");
|
|
49489
49491
|
function loadCfg() {
|
|
49490
49492
|
try {
|
|
49491
49493
|
return { ...DEFAULTS2, ...JSON.parse(fs3.readFileSync(FILE, "utf8")) };
|
|
@@ -49628,7 +49630,23 @@ function waitFor(c, event, match, timeoutMs = 4e3) {
|
|
|
49628
49630
|
}
|
|
49629
49631
|
var ok = (text) => ({ content: [{ type: "text", text }] });
|
|
49630
49632
|
var fail = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
49631
|
-
var
|
|
49633
|
+
var WATERMARK_FILE = np3.join(os4.homedir(), ".jefri", "inbox.json");
|
|
49634
|
+
var inboxWatermark = new Map(
|
|
49635
|
+
(() => {
|
|
49636
|
+
try {
|
|
49637
|
+
return Object.entries(JSON.parse(fs4.readFileSync(WATERMARK_FILE, "utf8")));
|
|
49638
|
+
} catch {
|
|
49639
|
+
return [];
|
|
49640
|
+
}
|
|
49641
|
+
})()
|
|
49642
|
+
);
|
|
49643
|
+
function saveWatermark() {
|
|
49644
|
+
try {
|
|
49645
|
+
fs4.mkdirSync(np3.dirname(WATERMARK_FILE), { recursive: true });
|
|
49646
|
+
fs4.writeFileSync(WATERMARK_FILE, JSON.stringify(Object.fromEntries(inboxWatermark)));
|
|
49647
|
+
} catch {
|
|
49648
|
+
}
|
|
49649
|
+
}
|
|
49632
49650
|
function registerAcpTools(server, ctx) {
|
|
49633
49651
|
const ENABLE_E2E = process.env.ACP_ENABLE_E2E === "true";
|
|
49634
49652
|
const e2eServer = ENABLE_E2E ? server : { registerTool: () => void 0 };
|
|
@@ -50299,8 +50317,8 @@ ${cmd}`);
|
|
|
50299
50317
|
server.registerTool(
|
|
50300
50318
|
"jefri_inbox",
|
|
50301
50319
|
{
|
|
50302
|
-
title: "
|
|
50303
|
-
description: "
|
|
50320
|
+
title: "Read unread messages",
|
|
50321
|
+
description: "Read your UNREAD / new messages \u2014 everything people and agents have sent you (DMs + group messages) since the last time you checked. Use this when asked to 'read my messages', 'check new messages', or 'catch up'. Each line is timestamped. Reads from the hub, so it works even without a live connection, and it remembers what you've already seen (persisted across restarts).",
|
|
50304
50322
|
inputSchema: {}
|
|
50305
50323
|
},
|
|
50306
50324
|
async () => withClient(async (c) => {
|
|
@@ -50320,6 +50338,7 @@ ${cmd}`);
|
|
|
50320
50338
|
if (!msgs.length) return ok("\u{1F4ED} No new messages.");
|
|
50321
50339
|
msgs.sort((a, b) => a.createdAt < b.createdAt ? -1 : 1);
|
|
50322
50340
|
inboxWatermark.set(me, msgs[msgs.length - 1].createdAt);
|
|
50341
|
+
saveWatermark();
|
|
50323
50342
|
const groupNames = /* @__PURE__ */ new Map();
|
|
50324
50343
|
if (msgs.some((m) => m.groupId)) {
|
|
50325
50344
|
try {
|
package/dist/index.js
CHANGED
|
@@ -25387,16 +25387,27 @@ function inQuietHours() {
|
|
|
25387
25387
|
return from < to ? cur >= from && cur < to : cur >= from || cur < to;
|
|
25388
25388
|
}
|
|
25389
25389
|
var clean = (s) => (s || "").replace(/[\r\n\t]+/g, " ").replace(/[\x00-\x1F]/g, "").slice(0, 140).trim();
|
|
25390
|
+
var LOG_FILE = np.join(os2.homedir(), ".jefri", "notify.log");
|
|
25391
|
+
function logNotify(line) {
|
|
25392
|
+
try {
|
|
25393
|
+
fs2.mkdirSync(np.dirname(LOG_FILE), { recursive: true });
|
|
25394
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
25395
|
+
fs2.appendFileSync(LOG_FILE, `[${ts}] ${line}
|
|
25396
|
+
`);
|
|
25397
|
+
} catch {
|
|
25398
|
+
}
|
|
25399
|
+
}
|
|
25390
25400
|
function notifyIncoming(m) {
|
|
25391
|
-
if (OFF || !prefs.enabled) return;
|
|
25392
|
-
if (m.isGroup ? !prefs.groups : !prefs.dms) return;
|
|
25393
|
-
if (m.isGroup && prefs.onlyMentions && !m.isMention) return;
|
|
25394
|
-
if (m.isGroup && m.groupId && prefs.mutedGroups.includes(m.groupId)) return;
|
|
25395
|
-
if (prefs.mutedUsers.includes(m.sender)) return;
|
|
25396
|
-
if (inQuietHours()) return;
|
|
25401
|
+
if (OFF || !prefs.enabled) return logNotify(`skip (disabled) from @${m.sender}`);
|
|
25402
|
+
if (m.isGroup ? !prefs.groups : !prefs.dms) return logNotify(`skip (dms/groups pref) from @${m.sender}`);
|
|
25403
|
+
if (m.isGroup && prefs.onlyMentions && !m.isMention) return logNotify(`skip (onlyMentions) from @${m.sender}`);
|
|
25404
|
+
if (m.isGroup && m.groupId && prefs.mutedGroups.includes(m.groupId)) return logNotify(`skip (muted group) from @${m.sender}`);
|
|
25405
|
+
if (prefs.mutedUsers.includes(m.sender)) return logNotify(`skip (muted user) @${m.sender}`);
|
|
25406
|
+
if (inQuietHours()) return logNotify(`skip (quiet hours) from @${m.sender}`);
|
|
25397
25407
|
const sound = prefs.sound;
|
|
25398
25408
|
const title = "Jefri Chat";
|
|
25399
25409
|
const body = clean(m.body);
|
|
25410
|
+
logNotify(`fire on ${process.platform} for @${m.sender}: ${body.slice(0, 60)}`);
|
|
25400
25411
|
if (sound) {
|
|
25401
25412
|
try {
|
|
25402
25413
|
process.stderr.write("\x07");
|
|
@@ -25420,9 +25431,21 @@ function notifyIncoming(m) {
|
|
|
25420
25431
|
const xesc = (x) => x.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
25421
25432
|
const audio = sound ? `<audio src="ms-winsoundevent:Notification.Default"/>` : `<audio silent="true"/>`;
|
|
25422
25433
|
const xml = `<toast><visual><binding template="ToastGeneric"><text>${xesc(title)}</text><text>${xesc(body)}</text></binding></visual>${audio}</toast>`;
|
|
25423
|
-
const ps = `$ErrorActionPreference='Stop';$t='${esc2(title)}';$b='${esc2(body)}';try{[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null;[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom, ContentType = WindowsRuntime] | Out-Null;$d=New-Object Windows.Data.Xml.Dom.XmlDocument;$d.LoadXml('${xml}');$toast=New-Object Windows.UI.Notifications.ToastNotification -ArgumentList $d;[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe').Show($toast);Start-Sleep -Milliseconds 400;}catch{Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;$n.BalloonTipTitle=$t;$n.BalloonTipText=$b;$n.Visible=$true;$n.ShowBalloonTip(6000);Start-Sleep -Milliseconds 6500;$n.Dispose();}`;
|
|
25434
|
+
const ps = `$ErrorActionPreference='Stop';$t='${esc2(title)}';$b='${esc2(body)}';try{[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null;[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom, ContentType = WindowsRuntime] | Out-Null;$d=New-Object Windows.Data.Xml.Dom.XmlDocument;$d.LoadXml('${xml}');$toast=New-Object Windows.UI.Notifications.ToastNotification -ArgumentList $d;[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe').Show($toast);Start-Sleep -Milliseconds 400;}catch{Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$n=New-Object System.Windows.Forms.NotifyIcon;$n.Icon=[System.Drawing.SystemIcons]::Information;$n.BalloonTipTitle=$t;$n.BalloonTipText=$b;$n.Visible=$true;$n.ShowBalloonTip(6000);Start-Sleep -Milliseconds 6500;$n.Dispose();[Console]::Error.WriteLine('TOASTERR-fell-back-to-balloon: '+$_.Exception.Message);}`;
|
|
25424
25435
|
const encoded = Buffer.from(ps, "utf16le").toString("base64");
|
|
25425
|
-
|
|
25436
|
+
const child = cp.spawn(
|
|
25437
|
+
"powershell",
|
|
25438
|
+
["-NoProfile", "-WindowStyle", "Hidden", "-EncodedCommand", encoded],
|
|
25439
|
+
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
25440
|
+
);
|
|
25441
|
+
let errBuf = "";
|
|
25442
|
+
child.stderr?.on("data", (d) => errBuf += d.toString());
|
|
25443
|
+
child.on("error", (e) => logNotify(`win: powershell spawn FAILED: ${e.message}`));
|
|
25444
|
+
child.on(
|
|
25445
|
+
"close",
|
|
25446
|
+
(code) => logNotify(`win: powershell exit=${code}${errBuf.trim() ? ` stderr=${errBuf.trim().slice(0, 300)}` : " (no error output)"}`)
|
|
25447
|
+
);
|
|
25448
|
+
child.unref();
|
|
25426
25449
|
} else {
|
|
25427
25450
|
cp.spawn("notify-send", ["-a", "Jefri Chat", title, body], { stdio: "ignore", detached: true }).unref();
|
|
25428
25451
|
}
|
|
@@ -25443,18 +25466,19 @@ var DEFAULTS2 = {
|
|
|
25443
25466
|
replyMode: "mentions",
|
|
25444
25467
|
persona: "",
|
|
25445
25468
|
replyToBots: false,
|
|
25446
|
-
ownerOnly:
|
|
25469
|
+
ownerOnly: true,
|
|
25470
|
+
// SECURITY: default to only the owner can direct an autonomous agent
|
|
25447
25471
|
contextMessages: 12,
|
|
25448
25472
|
promptTemplate: DEFAULT_TEMPLATE
|
|
25449
25473
|
};
|
|
25450
25474
|
var DIR = np2.join(os3.homedir(), ".jefri");
|
|
25451
25475
|
var FILE = np2.join(DIR, "autonomous.json");
|
|
25452
|
-
var
|
|
25476
|
+
var LOG_FILE2 = np2.join(DIR, "autonomous.log");
|
|
25453
25477
|
function logLine(line) {
|
|
25454
25478
|
try {
|
|
25455
25479
|
fs3.mkdirSync(DIR, { recursive: true });
|
|
25456
25480
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
25457
|
-
fs3.appendFileSync(
|
|
25481
|
+
fs3.appendFileSync(LOG_FILE2, `[${ts}] ${line}
|
|
25458
25482
|
`);
|
|
25459
25483
|
} catch {
|
|
25460
25484
|
}
|
|
@@ -25497,10 +25521,12 @@ function brainArgv(brain, prompt) {
|
|
|
25497
25521
|
var MAX_PROMPT = 12e3;
|
|
25498
25522
|
var MAX_REPLY = 7900;
|
|
25499
25523
|
var TIMEOUT_MS = 5 * 60 * 1e3;
|
|
25524
|
+
var FORCE_OWNER_ONLY = true;
|
|
25500
25525
|
function shouldHandle(m) {
|
|
25501
25526
|
if (!cfg.enabled) return false;
|
|
25502
25527
|
const fromOwner = !!ownerName && m.sender === ownerName;
|
|
25503
|
-
|
|
25528
|
+
const ownerOnly = cfg.ownerOnly || FORCE_OWNER_ONLY && !!ownerName;
|
|
25529
|
+
if (ownerOnly && !fromOwner) return false;
|
|
25504
25530
|
if (m.senderIsBot && !cfg.replyToBots) return false;
|
|
25505
25531
|
if (!m.isGroup) return true;
|
|
25506
25532
|
if (cfg.replyMode === "all") return true;
|
|
@@ -25719,7 +25745,23 @@ function waitFor(c, event, match, timeoutMs = 4e3) {
|
|
|
25719
25745
|
}
|
|
25720
25746
|
var ok = (text) => ({ content: [{ type: "text", text }] });
|
|
25721
25747
|
var fail = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
25722
|
-
var
|
|
25748
|
+
var WATERMARK_FILE = np3.join(os4.homedir(), ".jefri", "inbox.json");
|
|
25749
|
+
var inboxWatermark = new Map(
|
|
25750
|
+
(() => {
|
|
25751
|
+
try {
|
|
25752
|
+
return Object.entries(JSON.parse(fs4.readFileSync(WATERMARK_FILE, "utf8")));
|
|
25753
|
+
} catch {
|
|
25754
|
+
return [];
|
|
25755
|
+
}
|
|
25756
|
+
})()
|
|
25757
|
+
);
|
|
25758
|
+
function saveWatermark() {
|
|
25759
|
+
try {
|
|
25760
|
+
fs4.mkdirSync(np3.dirname(WATERMARK_FILE), { recursive: true });
|
|
25761
|
+
fs4.writeFileSync(WATERMARK_FILE, JSON.stringify(Object.fromEntries(inboxWatermark)));
|
|
25762
|
+
} catch {
|
|
25763
|
+
}
|
|
25764
|
+
}
|
|
25723
25765
|
function registerAcpTools(server2, ctx) {
|
|
25724
25766
|
const ENABLE_E2E = process.env.ACP_ENABLE_E2E === "true";
|
|
25725
25767
|
const e2eServer = ENABLE_E2E ? server2 : { registerTool: () => void 0 };
|
|
@@ -26390,8 +26432,8 @@ ${cmd}`);
|
|
|
26390
26432
|
server2.registerTool(
|
|
26391
26433
|
"jefri_inbox",
|
|
26392
26434
|
{
|
|
26393
|
-
title: "
|
|
26394
|
-
description: "
|
|
26435
|
+
title: "Read unread messages",
|
|
26436
|
+
description: "Read your UNREAD / new messages \u2014 everything people and agents have sent you (DMs + group messages) since the last time you checked. Use this when asked to 'read my messages', 'check new messages', or 'catch up'. Each line is timestamped. Reads from the hub, so it works even without a live connection, and it remembers what you've already seen (persisted across restarts).",
|
|
26395
26437
|
inputSchema: {}
|
|
26396
26438
|
},
|
|
26397
26439
|
async () => withClient(async (c) => {
|
|
@@ -26411,6 +26453,7 @@ ${cmd}`);
|
|
|
26411
26453
|
if (!msgs.length) return ok("\u{1F4ED} No new messages.");
|
|
26412
26454
|
msgs.sort((a, b) => a.createdAt < b.createdAt ? -1 : 1);
|
|
26413
26455
|
inboxWatermark.set(me, msgs[msgs.length - 1].createdAt);
|
|
26456
|
+
saveWatermark();
|
|
26414
26457
|
const groupNames = /* @__PURE__ */ new Map();
|
|
26415
26458
|
if (msgs.some((m) => m.groupId)) {
|
|
26416
26459
|
try {
|
package/package.json
CHANGED