jefrichat-mcp 0.48.6 → 0.48.8
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 +163 -53
- package/dist/index.js +402 -47
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -20888,8 +20888,8 @@ var require_application = __commonJS({
|
|
|
20888
20888
|
tryRender(view, renderOptions, done);
|
|
20889
20889
|
};
|
|
20890
20890
|
app2.listen = function listen() {
|
|
20891
|
-
var
|
|
20892
|
-
return
|
|
20891
|
+
var server2 = http2.createServer(this);
|
|
20892
|
+
return server2.listen.apply(server2, arguments);
|
|
20893
20893
|
};
|
|
20894
20894
|
function logerror(err) {
|
|
20895
20895
|
if (this.get("env") !== "test") console.error(err.stack || err.toString());
|
|
@@ -33214,10 +33214,10 @@ var require_websocket_server = __commonJS({
|
|
|
33214
33214
|
process.nextTick(emitClose, this);
|
|
33215
33215
|
}
|
|
33216
33216
|
} else {
|
|
33217
|
-
const
|
|
33217
|
+
const server2 = this._server;
|
|
33218
33218
|
this._removeListeners();
|
|
33219
33219
|
this._removeListeners = this._server = null;
|
|
33220
|
-
|
|
33220
|
+
server2.close(() => {
|
|
33221
33221
|
emitClose(this);
|
|
33222
33222
|
});
|
|
33223
33223
|
}
|
|
@@ -33402,17 +33402,17 @@ var require_websocket_server = __commonJS({
|
|
|
33402
33402
|
}
|
|
33403
33403
|
};
|
|
33404
33404
|
module.exports = WebSocketServer2;
|
|
33405
|
-
function addListeners(
|
|
33406
|
-
for (const event of Object.keys(map3))
|
|
33405
|
+
function addListeners(server2, map3) {
|
|
33406
|
+
for (const event of Object.keys(map3)) server2.on(event, map3[event]);
|
|
33407
33407
|
return function removeListeners() {
|
|
33408
33408
|
for (const event of Object.keys(map3)) {
|
|
33409
|
-
|
|
33409
|
+
server2.removeListener(event, map3[event]);
|
|
33410
33410
|
}
|
|
33411
33411
|
};
|
|
33412
33412
|
}
|
|
33413
|
-
function emitClose(
|
|
33414
|
-
|
|
33415
|
-
|
|
33413
|
+
function emitClose(server2) {
|
|
33414
|
+
server2._state = CLOSED;
|
|
33415
|
+
server2.emit("close");
|
|
33416
33416
|
}
|
|
33417
33417
|
function socketOnError() {
|
|
33418
33418
|
this.destroy();
|
|
@@ -33431,11 +33431,11 @@ var require_websocket_server = __commonJS({
|
|
|
33431
33431
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
33432
33432
|
);
|
|
33433
33433
|
}
|
|
33434
|
-
function abortHandshakeOrEmitwsClientError(
|
|
33435
|
-
if (
|
|
33434
|
+
function abortHandshakeOrEmitwsClientError(server2, req, socket, code3, message, headers) {
|
|
33435
|
+
if (server2.listenerCount("wsClientError")) {
|
|
33436
33436
|
const err = new Error(message);
|
|
33437
33437
|
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
|
33438
|
-
|
|
33438
|
+
server2.emit("wsClientError", err, socket, req);
|
|
33439
33439
|
} else {
|
|
33440
33440
|
abortHandshake(socket, code3, message, headers);
|
|
33441
33441
|
}
|
|
@@ -60697,15 +60697,15 @@ var JefriClient = class _JefriClient {
|
|
|
60697
60697
|
get online() {
|
|
60698
60698
|
return this.hasConnected && !this.closing && this.ws?.readyState === this.ws?.OPEN;
|
|
60699
60699
|
}
|
|
60700
|
-
constructor(
|
|
60701
|
-
this.server =
|
|
60700
|
+
constructor(server2) {
|
|
60701
|
+
this.server = server2.replace(/\/$/, "");
|
|
60702
60702
|
}
|
|
60703
60703
|
static async connect(opts = {}) {
|
|
60704
|
-
const
|
|
60705
|
-
const client = new _JefriClient(
|
|
60704
|
+
const server2 = (opts.server ?? "https://jefrichat.com").replace(/\/$/, "");
|
|
60705
|
+
const client = new _JefriClient(server2);
|
|
60706
60706
|
let token = opts.token;
|
|
60707
60707
|
if (!token) {
|
|
60708
|
-
const res = await fetch(`${
|
|
60708
|
+
const res = await fetch(`${server2}/api/agents`, {
|
|
60709
60709
|
method: "POST",
|
|
60710
60710
|
headers: { "content-type": "application/json" },
|
|
60711
60711
|
body: JSON.stringify({
|
|
@@ -60976,6 +60976,28 @@ var JefriClient = class _JefriClient {
|
|
|
60976
60976
|
announceHost(host) {
|
|
60977
60977
|
const h = (host ?? "").trim();
|
|
60978
60978
|
this.clientInfo = { ...this.clientInfo ?? { name: "jefri-sdk", version: "0" }, host: h };
|
|
60979
|
+
this.sendClientInfo();
|
|
60980
|
+
}
|
|
60981
|
+
/** Announce WHERE this connector runs — the folder and the machine.
|
|
60982
|
+
*
|
|
60983
|
+
* Separate from announceHost because the two are learned at different times
|
|
60984
|
+
* and by different means: the host app arrives from the MCP handshake, the
|
|
60985
|
+
* location is simply observed. Callers pass values they READ NOW
|
|
60986
|
+
* (process.cwd(), os.hostname()) — never a configured preference, which is
|
|
60987
|
+
* what made the old reported folder wrong. Omitted fields are left untouched
|
|
60988
|
+
* by the hub rather than cleared, so a caller that knows only one may send
|
|
60989
|
+
* only that one. */
|
|
60990
|
+
announceWhere(where) {
|
|
60991
|
+
const base = this.clientInfo ?? { name: "jefri-sdk", version: "0" };
|
|
60992
|
+
const next = { ...base };
|
|
60993
|
+
if (where.cwd !== void 0) next.cwd = (where.cwd ?? "").trim();
|
|
60994
|
+
if (where.machine !== void 0) next.machine = (where.machine ?? "").trim();
|
|
60995
|
+
this.clientInfo = next;
|
|
60996
|
+
this.sendClientInfo();
|
|
60997
|
+
}
|
|
60998
|
+
/** Push the current clientInfo if the socket is live. Reconnects re-send it
|
|
60999
|
+
* from the stored copy, so a failure here is not worth reporting. */
|
|
61000
|
+
sendClientInfo() {
|
|
60979
61001
|
try {
|
|
60980
61002
|
if (this.ws?.readyState === this.ws?.OPEN)
|
|
60981
61003
|
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
@@ -61983,7 +62005,21 @@ function waitFor(c, event, match, timeoutMs = 4e3) {
|
|
|
61983
62005
|
}, timeoutMs);
|
|
61984
62006
|
});
|
|
61985
62007
|
}
|
|
61986
|
-
var
|
|
62008
|
+
var upgradeNotice = null;
|
|
62009
|
+
var upgradeShownAt = 0;
|
|
62010
|
+
var UPGRADE_REPEAT_MS = 24 * 60 * 60 * 1e3;
|
|
62011
|
+
function takeUpgradeNotice(now = Date.now()) {
|
|
62012
|
+
if (!upgradeNotice) return null;
|
|
62013
|
+
if (upgradeShownAt && now - upgradeShownAt < UPGRADE_REPEAT_MS) return null;
|
|
62014
|
+
upgradeShownAt = now;
|
|
62015
|
+
return upgradeNotice;
|
|
62016
|
+
}
|
|
62017
|
+
var ok3 = (text5, now = Date.now()) => {
|
|
62018
|
+
const notice = takeUpgradeNotice(now);
|
|
62019
|
+
return { content: [{ type: "text", text: notice ? `${text5}
|
|
62020
|
+
|
|
62021
|
+
${notice}` : text5 }] };
|
|
62022
|
+
};
|
|
61987
62023
|
var fail = (text5) => ({ content: [{ type: "text", text: text5 }], isError: true });
|
|
61988
62024
|
var WATERMARK_FILE = np3.join(os4.homedir(), ".jefri", "inbox.json");
|
|
61989
62025
|
var inboxWatermark = new Map(
|
|
@@ -62002,9 +62038,9 @@ function saveWatermark() {
|
|
|
62002
62038
|
} catch {
|
|
62003
62039
|
}
|
|
62004
62040
|
}
|
|
62005
|
-
function registerAcpTools(
|
|
62041
|
+
function registerAcpTools(server2, ctx) {
|
|
62006
62042
|
const ENABLE_E2E = process.env.JEFRI_ENABLE_E2E === "true";
|
|
62007
|
-
const e2eServer = ENABLE_E2E ?
|
|
62043
|
+
const e2eServer = ENABLE_E2E ? server2 : { registerTool: () => void 0 };
|
|
62008
62044
|
const withClient = async (fn) => {
|
|
62009
62045
|
try {
|
|
62010
62046
|
const c = await ctx.ensureClient();
|
|
@@ -62014,7 +62050,7 @@ function registerAcpTools(server, ctx) {
|
|
|
62014
62050
|
${e?.message ?? e}`);
|
|
62015
62051
|
}
|
|
62016
62052
|
};
|
|
62017
|
-
|
|
62053
|
+
server2.registerTool(
|
|
62018
62054
|
"jefri_whoami",
|
|
62019
62055
|
{
|
|
62020
62056
|
title: "Who am I on Jefri Chat",
|
|
@@ -62025,7 +62061,7 @@ ${e?.message ?? e}`);
|
|
|
62025
62061
|
async (c) => ok3(`You are "${c.identity.displayName}" (@${c.identity.username}) on Jefri Chat at ${ctx.serverUrl}, status: online.`)
|
|
62026
62062
|
)
|
|
62027
62063
|
);
|
|
62028
|
-
|
|
62064
|
+
server2.registerTool(
|
|
62029
62065
|
"jefri_agents",
|
|
62030
62066
|
{
|
|
62031
62067
|
title: "List your connections",
|
|
@@ -62061,7 +62097,7 @@ ${e?.message ?? e}`);
|
|
|
62061
62097
|
}
|
|
62062
62098
|
return { status: res.status, body };
|
|
62063
62099
|
};
|
|
62064
|
-
|
|
62100
|
+
server2.registerTool(
|
|
62065
62101
|
"jefri_agent_apps",
|
|
62066
62102
|
{
|
|
62067
62103
|
title: "See where your agents are running",
|
|
@@ -62085,7 +62121,7 @@ ${lines.join("\n")}
|
|
|
62085
62121
|
Use jefri_agent_apps with agent=<name> to see its live app + active state.`);
|
|
62086
62122
|
})
|
|
62087
62123
|
);
|
|
62088
|
-
|
|
62124
|
+
server2.registerTool(
|
|
62089
62125
|
"jefri_deactivate",
|
|
62090
62126
|
{
|
|
62091
62127
|
title: "Deactivate an agent",
|
|
@@ -62098,7 +62134,7 @@ Use jefri_agent_apps with agent=<name> to see its live app + active state.`);
|
|
|
62098
62134
|
return ok3(`\u{1F6D1} Deactivated @${agent}. It's disconnected and can't reconnect from any app until you reactivate it.`);
|
|
62099
62135
|
})
|
|
62100
62136
|
);
|
|
62101
|
-
|
|
62137
|
+
server2.registerTool(
|
|
62102
62138
|
"jefri_activate",
|
|
62103
62139
|
{
|
|
62104
62140
|
title: "Reactivate an agent",
|
|
@@ -62111,7 +62147,7 @@ Use jefri_agent_apps with agent=<name> to see its live app + active state.`);
|
|
|
62111
62147
|
return ok3(`\u2705 Reactivated @${agent}. It can connect again now.`);
|
|
62112
62148
|
})
|
|
62113
62149
|
);
|
|
62114
|
-
|
|
62150
|
+
server2.registerTool(
|
|
62115
62151
|
"jefri_send",
|
|
62116
62152
|
{
|
|
62117
62153
|
title: "Send a message",
|
|
@@ -62264,7 +62300,7 @@ ${fp}`);
|
|
|
62264
62300
|
return ok3(`Sent private E2E group file.`);
|
|
62265
62301
|
})
|
|
62266
62302
|
);
|
|
62267
|
-
|
|
62303
|
+
server2.registerTool(
|
|
62268
62304
|
"jefri_send_file",
|
|
62269
62305
|
{
|
|
62270
62306
|
title: "Send a file, PDF, or image",
|
|
@@ -62348,7 +62384,7 @@ ${psCmd}
|
|
|
62348
62384
|
return ok3(`Sent \u{1F4CE} ${name} to @${to}${caption ? ` \u2014 "${caption}"` : ""}`);
|
|
62349
62385
|
})
|
|
62350
62386
|
);
|
|
62351
|
-
|
|
62387
|
+
server2.registerTool(
|
|
62352
62388
|
"jefri_send_code",
|
|
62353
62389
|
{
|
|
62354
62390
|
title: "Send code / text as a file",
|
|
@@ -62383,7 +62419,7 @@ ${psCmd}
|
|
|
62383
62419
|
return ok3(`Sent \u{1F4C4} ${fileName} (${content3.length} chars) to @${to}${caption ? ` \u2014 "${caption}"` : ""}`);
|
|
62384
62420
|
})
|
|
62385
62421
|
);
|
|
62386
|
-
|
|
62422
|
+
server2.registerTool(
|
|
62387
62423
|
"jefri_send_folder",
|
|
62388
62424
|
{
|
|
62389
62425
|
title: "Send a whole folder / project (zipped)",
|
|
@@ -62481,7 +62517,7 @@ ${psCmd}
|
|
|
62481
62517
|
);
|
|
62482
62518
|
})
|
|
62483
62519
|
);
|
|
62484
|
-
|
|
62520
|
+
server2.registerTool(
|
|
62485
62521
|
"jefri_download_file",
|
|
62486
62522
|
{
|
|
62487
62523
|
title: "Download / save a file someone sent you",
|
|
@@ -62528,7 +62564,7 @@ It downloads the file to your machine (current folder unless you set a path). Fi
|
|
|
62528
62564
|
);
|
|
62529
62565
|
})
|
|
62530
62566
|
);
|
|
62531
|
-
|
|
62567
|
+
server2.registerTool(
|
|
62532
62568
|
"jefri_connect",
|
|
62533
62569
|
{
|
|
62534
62570
|
title: "Request a connection",
|
|
@@ -62544,7 +62580,7 @@ It downloads the file to your machine (current folder unless you set a path). Fi
|
|
|
62544
62580
|
})
|
|
62545
62581
|
);
|
|
62546
62582
|
const hubBase = () => ctx.serverUrl.replace(/\/$/, "");
|
|
62547
|
-
|
|
62583
|
+
server2.registerTool(
|
|
62548
62584
|
"jefri_departments",
|
|
62549
62585
|
{
|
|
62550
62586
|
title: "List your departments",
|
|
@@ -62558,7 +62594,7 @@ It downloads the file to your machine (current folder unless you set a path). Fi
|
|
|
62558
62594
|
return ok3(d.map((x) => `\u2022 ${x.name} (${x.org}) \u2014 access: ${x.myLevel} [id: ${x.id}]`).join("\n"));
|
|
62559
62595
|
})
|
|
62560
62596
|
);
|
|
62561
|
-
|
|
62597
|
+
server2.registerTool(
|
|
62562
62598
|
"jefri_dept_files",
|
|
62563
62599
|
{
|
|
62564
62600
|
title: "List a department's files",
|
|
@@ -62573,7 +62609,7 @@ It downloads the file to your machine (current folder unless you set a path). Fi
|
|
|
62573
62609
|
return ok3(files.map((f) => `\u2022 ${f.name} [${f.kind}] \u2014 by ${f.createdBy}, ${new Date(f.updatedAt).toLocaleDateString()} (id: ${f.id})`).join("\n"));
|
|
62574
62610
|
})
|
|
62575
62611
|
);
|
|
62576
|
-
|
|
62612
|
+
server2.registerTool(
|
|
62577
62613
|
"jefri_dept_read",
|
|
62578
62614
|
{
|
|
62579
62615
|
title: "Download a department file",
|
|
@@ -62603,7 +62639,7 @@ ${cmd}
|
|
|
62603
62639
|
(Set your token first: export JEFRI_TOKEN=\u2026 from the app's Connect dialog \u2014 it isn't printed here on purpose.)`);
|
|
62604
62640
|
})
|
|
62605
62641
|
);
|
|
62606
|
-
|
|
62642
|
+
server2.registerTool(
|
|
62607
62643
|
"jefri_dept_add_file",
|
|
62608
62644
|
{
|
|
62609
62645
|
title: "Add a file to a department",
|
|
@@ -62626,7 +62662,7 @@ ${cmd}
|
|
|
62626
62662
|
(With 'write' access it's added directly; with 'propose' access it's submitted for an admin to approve; read-only is rejected.)`);
|
|
62627
62663
|
})
|
|
62628
62664
|
);
|
|
62629
|
-
|
|
62665
|
+
server2.registerTool(
|
|
62630
62666
|
"jefri_my_docs",
|
|
62631
62667
|
{
|
|
62632
62668
|
title: "List my documents (memory)",
|
|
@@ -62645,7 +62681,7 @@ ${cmd}
|
|
|
62645
62681
|
);
|
|
62646
62682
|
})
|
|
62647
62683
|
);
|
|
62648
|
-
|
|
62684
|
+
server2.registerTool(
|
|
62649
62685
|
"jefri_search_docs",
|
|
62650
62686
|
{
|
|
62651
62687
|
title: "Search my document memory",
|
|
@@ -62668,7 +62704,7 @@ ${h.text}`).join("\n\n---\n\n")
|
|
|
62668
62704
|
);
|
|
62669
62705
|
})
|
|
62670
62706
|
);
|
|
62671
|
-
|
|
62707
|
+
server2.registerTool(
|
|
62672
62708
|
"jefri_read_doc",
|
|
62673
62709
|
{
|
|
62674
62710
|
title: "Read/get one of my documents",
|
|
@@ -62693,7 +62729,7 @@ ${cmd}
|
|
|
62693
62729
|
(Set your token first: export JEFRI_TOKEN=\u2026 from the app's Connect dialog \u2014 it isn't printed here on purpose.)`);
|
|
62694
62730
|
})
|
|
62695
62731
|
);
|
|
62696
|
-
|
|
62732
|
+
server2.registerTool(
|
|
62697
62733
|
"jefri_add_doc",
|
|
62698
62734
|
{
|
|
62699
62735
|
title: "Add a document to my memory",
|
|
@@ -62713,7 +62749,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62713
62749
|
);
|
|
62714
62750
|
})
|
|
62715
62751
|
);
|
|
62716
|
-
|
|
62752
|
+
server2.registerTool(
|
|
62717
62753
|
"jefri_my_tasks",
|
|
62718
62754
|
{
|
|
62719
62755
|
title: "My department tasks",
|
|
@@ -62730,7 +62766,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62730
62766
|
);
|
|
62731
62767
|
})
|
|
62732
62768
|
);
|
|
62733
|
-
|
|
62769
|
+
server2.registerTool(
|
|
62734
62770
|
"jefri_task_status",
|
|
62735
62771
|
{
|
|
62736
62772
|
title: "Update a task's status",
|
|
@@ -62752,7 +62788,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62752
62788
|
return ok3(`Task marked ${status === "done" ? "\u2705 done" : status}.`);
|
|
62753
62789
|
})
|
|
62754
62790
|
);
|
|
62755
|
-
|
|
62791
|
+
server2.registerTool(
|
|
62756
62792
|
"jefri_inbox",
|
|
62757
62793
|
{
|
|
62758
62794
|
title: "Read unread messages",
|
|
@@ -62808,7 +62844,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62808
62844
|
` + lines.join("\n"));
|
|
62809
62845
|
})
|
|
62810
62846
|
);
|
|
62811
|
-
|
|
62847
|
+
server2.registerTool(
|
|
62812
62848
|
"jefri_groups",
|
|
62813
62849
|
{
|
|
62814
62850
|
title: "List your groups",
|
|
@@ -62824,7 +62860,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62824
62860
|
);
|
|
62825
62861
|
})
|
|
62826
62862
|
);
|
|
62827
|
-
|
|
62863
|
+
server2.registerTool(
|
|
62828
62864
|
"jefri_send_group",
|
|
62829
62865
|
{
|
|
62830
62866
|
title: "Send a group message",
|
|
@@ -62842,7 +62878,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62842
62878
|
return ok3(`Posted to the group.`);
|
|
62843
62879
|
})
|
|
62844
62880
|
);
|
|
62845
|
-
|
|
62881
|
+
server2.registerTool(
|
|
62846
62882
|
"jefri_send_group_file",
|
|
62847
62883
|
{
|
|
62848
62884
|
title: "Send a file to a group",
|
|
@@ -62916,7 +62952,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62916
62952
|
return ok3(`Sent \u{1F4CE} ${name} to the group.`);
|
|
62917
62953
|
})
|
|
62918
62954
|
);
|
|
62919
|
-
|
|
62955
|
+
server2.registerTool(
|
|
62920
62956
|
"jefri_history",
|
|
62921
62957
|
{
|
|
62922
62958
|
title: "Read conversation history",
|
|
@@ -62956,7 +62992,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62956
62992
|
return ok3(lines.join("\n"));
|
|
62957
62993
|
})
|
|
62958
62994
|
);
|
|
62959
|
-
|
|
62995
|
+
server2.registerTool(
|
|
62960
62996
|
"jefri_search",
|
|
62961
62997
|
{
|
|
62962
62998
|
title: "Discover agents",
|
|
@@ -62974,7 +63010,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62974
63010
|
);
|
|
62975
63011
|
})
|
|
62976
63012
|
);
|
|
62977
|
-
|
|
63013
|
+
server2.registerTool(
|
|
62978
63014
|
"jefri_set_status",
|
|
62979
63015
|
{
|
|
62980
63016
|
title: "Set presence",
|
|
@@ -62989,7 +63025,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
62989
63025
|
})
|
|
62990
63026
|
);
|
|
62991
63027
|
if (ctx.local) {
|
|
62992
|
-
|
|
63028
|
+
server2.registerTool(
|
|
62993
63029
|
"jefri_notifications",
|
|
62994
63030
|
{
|
|
62995
63031
|
title: "Notification settings",
|
|
@@ -63068,7 +63104,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
63068
63104
|
return ok3(summary);
|
|
63069
63105
|
})
|
|
63070
63106
|
);
|
|
63071
|
-
|
|
63107
|
+
server2.registerTool(
|
|
63072
63108
|
"jefri_autonomous",
|
|
63073
63109
|
{
|
|
63074
63110
|
title: "Autonomous mode",
|
|
@@ -63145,6 +63181,34 @@ function attachInbox(c, inbox, self, onIncoming) {
|
|
|
63145
63181
|
c.on("file_received", capture);
|
|
63146
63182
|
}
|
|
63147
63183
|
|
|
63184
|
+
// src/drain.ts
|
|
63185
|
+
function durationEnv(name, fallback, env = process.env) {
|
|
63186
|
+
const raw = env[name];
|
|
63187
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
63188
|
+
const n = Number(raw);
|
|
63189
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
63190
|
+
console.error(`[mcp] ignoring ${name}="${raw}" \u2014 expected a positive number; using ${fallback}ms`);
|
|
63191
|
+
return fallback;
|
|
63192
|
+
}
|
|
63193
|
+
return n;
|
|
63194
|
+
}
|
|
63195
|
+
function sweepIdleSessions(map3) {
|
|
63196
|
+
let closed = 0;
|
|
63197
|
+
let busy = 0;
|
|
63198
|
+
for (const s of map3) {
|
|
63199
|
+
if (s.inflight > 0) {
|
|
63200
|
+
busy++;
|
|
63201
|
+
continue;
|
|
63202
|
+
}
|
|
63203
|
+
try {
|
|
63204
|
+
s.transport.close?.();
|
|
63205
|
+
closed++;
|
|
63206
|
+
} catch {
|
|
63207
|
+
}
|
|
63208
|
+
}
|
|
63209
|
+
return { closed, busy };
|
|
63210
|
+
}
|
|
63211
|
+
|
|
63148
63212
|
// src/http.ts
|
|
63149
63213
|
var HUB = process.env.JEFRI_SERVER ?? "http://localhost:4000";
|
|
63150
63214
|
var PORT = Number(process.env.MCP_HTTP_PORT ?? 4001);
|
|
@@ -63288,7 +63352,14 @@ app.use((req, res, next) => {
|
|
|
63288
63352
|
next();
|
|
63289
63353
|
});
|
|
63290
63354
|
app.use(import_express.default.json({ limit: process.env.MCP_MAX_BODY ?? "4mb" }));
|
|
63291
|
-
var
|
|
63355
|
+
var draining = false;
|
|
63356
|
+
var health = (_req, res) => res.status(draining ? 503 : 200).json({
|
|
63357
|
+
ok: !draining,
|
|
63358
|
+
draining,
|
|
63359
|
+
service: "jefrichat-mcp-http",
|
|
63360
|
+
hub: HUB,
|
|
63361
|
+
sessions: sessions.size
|
|
63362
|
+
});
|
|
63292
63363
|
app.get("/health", health);
|
|
63293
63364
|
app.get("/mcp/health", health);
|
|
63294
63365
|
app.post("/mcp", async (req, res) => {
|
|
@@ -63466,13 +63537,52 @@ var bySession = async (req, res) => {
|
|
|
63466
63537
|
};
|
|
63467
63538
|
app.get("/mcp", bySession);
|
|
63468
63539
|
app.delete("/mcp", bySession);
|
|
63469
|
-
http.createServer(app).listen(PORT, () => {
|
|
63540
|
+
var server = http.createServer(app).listen(PORT, () => {
|
|
63470
63541
|
console.error(`
|
|
63471
63542
|
\u{1F310} Jefri Chat remote MCP (HTTP) on http://localhost:${PORT}/mcp`);
|
|
63472
63543
|
console.error(` hub: ${HUB}`);
|
|
63473
63544
|
console.error(` auth: Authorization: Bearer <jefri_token>
|
|
63474
63545
|
`);
|
|
63475
63546
|
});
|
|
63547
|
+
var DRAIN_MS = durationEnv("MCP_DRAIN_MS", 15e3);
|
|
63548
|
+
var UNHEALTHY_MS = durationEnv("MCP_UNHEALTHY_MS", 5e3);
|
|
63549
|
+
var shuttingDown = false;
|
|
63550
|
+
function drain2(signal) {
|
|
63551
|
+
if (shuttingDown) return;
|
|
63552
|
+
shuttingDown = true;
|
|
63553
|
+
draining = true;
|
|
63554
|
+
const deadline = Date.now() + UNHEALTHY_MS + DRAIN_MS;
|
|
63555
|
+
console.error(
|
|
63556
|
+
`[mcp] ${signal} \u2014 unhealthy for ${UNHEALTHY_MS}ms, then draining ${sessions.size} session(s) for up to ${DRAIN_MS}ms`
|
|
63557
|
+
);
|
|
63558
|
+
const closeAndFinish = () => {
|
|
63559
|
+
server.close(() => {
|
|
63560
|
+
console.error("[mcp] drained cleanly");
|
|
63561
|
+
process.exit(0);
|
|
63562
|
+
});
|
|
63563
|
+
const sweep = setInterval(() => {
|
|
63564
|
+
const { busy } = sweepIdleSessions(sessions.values());
|
|
63565
|
+
if (busy === 0) {
|
|
63566
|
+
clearInterval(sweep);
|
|
63567
|
+
console.error("[mcp] all sessions idle \u2014 closing");
|
|
63568
|
+
return;
|
|
63569
|
+
}
|
|
63570
|
+
if (Date.now() >= deadline) {
|
|
63571
|
+
clearInterval(sweep);
|
|
63572
|
+
console.error(`[mcp] drain deadline reached with ${busy} session(s) still busy \u2014 exiting`);
|
|
63573
|
+
process.exit(0);
|
|
63574
|
+
}
|
|
63575
|
+
}, 250);
|
|
63576
|
+
sweep.unref();
|
|
63577
|
+
};
|
|
63578
|
+
setTimeout(closeAndFinish, UNHEALTHY_MS).unref();
|
|
63579
|
+
setTimeout(() => {
|
|
63580
|
+
console.error("[mcp] drain deadline reached \u2014 exiting");
|
|
63581
|
+
process.exit(0);
|
|
63582
|
+
}, UNHEALTHY_MS + DRAIN_MS + 500).unref();
|
|
63583
|
+
}
|
|
63584
|
+
process.on("SIGTERM", () => drain2("SIGTERM"));
|
|
63585
|
+
process.on("SIGINT", () => drain2("SIGINT"));
|
|
63476
63586
|
/*! Bundled license information:
|
|
63477
63587
|
|
|
63478
63588
|
depd/index.js:
|
package/dist/index.js
CHANGED
|
@@ -10847,8 +10847,10 @@ var init_control_proto = __esm({
|
|
|
10847
10847
|
// {agent?, ids?} → {unread: UnreadTally}
|
|
10848
10848
|
"unread",
|
|
10849
10849
|
// {} → UnreadTally
|
|
10850
|
-
"subscribe"
|
|
10850
|
+
"subscribe",
|
|
10851
10851
|
// {} → {subscribed: true}; server then pushes EventFrames
|
|
10852
|
+
"history"
|
|
10853
|
+
// {agent, with?|groupId?, limit?} → {messages: HistoryMessage[]}
|
|
10852
10854
|
];
|
|
10853
10855
|
METHOD_SET = new Set(METHODS);
|
|
10854
10856
|
FrameDecoder = class {
|
|
@@ -11441,6 +11443,17 @@ async function openSession() {
|
|
|
11441
11443
|
return {
|
|
11442
11444
|
conns,
|
|
11443
11445
|
agents: () => conns.flatMap((c2) => c2.agents),
|
|
11446
|
+
async history(agent, q) {
|
|
11447
|
+
const conn = conns.find((c2) => c2.agents.some((a) => a.username === agent)) ?? conns[0];
|
|
11448
|
+
if (!conn) return [];
|
|
11449
|
+
const res = await conn.request("history", {
|
|
11450
|
+
agent,
|
|
11451
|
+
with: q.with,
|
|
11452
|
+
groupId: q.groupId,
|
|
11453
|
+
limit: q.limit ?? 50
|
|
11454
|
+
});
|
|
11455
|
+
return res.messages ?? [];
|
|
11456
|
+
},
|
|
11444
11457
|
async inbox(limit = 50) {
|
|
11445
11458
|
const per = await Promise.allSettled(
|
|
11446
11459
|
conns.map((c2) => c2.request("inbox", { limit }))
|
|
@@ -12104,9 +12117,9 @@ __export(run_exports, {
|
|
|
12104
12117
|
tmuxPanelSetupArgv: () => tmuxPanelSetupArgv,
|
|
12105
12118
|
tmuxRunArgv: () => tmuxRunArgv
|
|
12106
12119
|
});
|
|
12107
|
-
import
|
|
12120
|
+
import cp9 from "node:child_process";
|
|
12108
12121
|
import { createHash as createHash2 } from "node:crypto";
|
|
12109
|
-
import
|
|
12122
|
+
import readline2 from "node:readline";
|
|
12110
12123
|
function tmuxRunArgv(agentCmd, sessionName, cwd, envPairs = []) {
|
|
12111
12124
|
const env = envPairs.flatMap((e) => ["-e", e]);
|
|
12112
12125
|
return { cmd: "tmux", args: ["new-session", "-d", "-s", sessionName, "-c", cwd, ...env, "--", ...agentCmd] };
|
|
@@ -12157,7 +12170,7 @@ function tmuxPanelSetupArgv(session, launcher, badgeScript) {
|
|
|
12157
12170
|
}
|
|
12158
12171
|
function hasTmux() {
|
|
12159
12172
|
try {
|
|
12160
|
-
|
|
12173
|
+
cp9.execFileSync("tmux", ["-V"], { stdio: "ignore" });
|
|
12161
12174
|
return true;
|
|
12162
12175
|
} catch {
|
|
12163
12176
|
return false;
|
|
@@ -12175,9 +12188,9 @@ function resolveTmuxInstall(platform, has = hasCmd2) {
|
|
|
12175
12188
|
const command = mgr === "pacman" ? "sudo pacman -S tmux" : mgr === "apk" ? "sudo apk add tmux" : mgr ? `sudo ${mgr} install tmux` : "install tmux with your distro's package manager";
|
|
12176
12189
|
return { kind: "linux-manual", command };
|
|
12177
12190
|
}
|
|
12178
|
-
async function
|
|
12191
|
+
async function promptYesNoDefaultYes2(question) {
|
|
12179
12192
|
if (!process.stdin.isTTY) return false;
|
|
12180
|
-
const rl =
|
|
12193
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
12181
12194
|
try {
|
|
12182
12195
|
const ans = (await new Promise((res) => rl.question(question, res))).trim().toLowerCase();
|
|
12183
12196
|
return ans === "" || ans === "y" || ans === "yes";
|
|
@@ -12196,7 +12209,7 @@ async function ensureTmuxInteractive() {
|
|
|
12196
12209
|
`
|
|
12197
12210
|
);
|
|
12198
12211
|
if (plan.kind === "brew") {
|
|
12199
|
-
const yes = await
|
|
12212
|
+
const yes = await promptYesNoDefaultYes2(` Install tmux now with Homebrew? (no password / no sudo) [Y/n] `);
|
|
12200
12213
|
if (!yes) {
|
|
12201
12214
|
process.stderr.write(` No problem \u2014 install it any time with: ${plan.command}
|
|
12202
12215
|
`);
|
|
@@ -12205,7 +12218,7 @@ async function ensureTmuxInteractive() {
|
|
|
12205
12218
|
process.stderr.write(` Installing tmux via Homebrew\u2026
|
|
12206
12219
|
`);
|
|
12207
12220
|
try {
|
|
12208
|
-
|
|
12221
|
+
cp9.execFileSync("brew", ["install", "tmux"], { stdio: "inherit" });
|
|
12209
12222
|
} catch {
|
|
12210
12223
|
process.stderr.write(` \u26A0\uFE0F 'brew install tmux' didn't finish \u2014 install it manually (${plan.command}), then re-run this command.
|
|
12211
12224
|
`);
|
|
@@ -12236,7 +12249,7 @@ async function runWrapped(argv) {
|
|
|
12236
12249
|
return 2;
|
|
12237
12250
|
}
|
|
12238
12251
|
const spawnInherit = (cmd2, args2) => new Promise((res) => {
|
|
12239
|
-
const child =
|
|
12252
|
+
const child = cp9.spawn(cmd2, args2, { stdio: "inherit" });
|
|
12240
12253
|
child.on("error", (e) => {
|
|
12241
12254
|
process.stderr.write(`jefri run: could not start ${cmd2}: ${e?.message ?? e}
|
|
12242
12255
|
`);
|
|
@@ -12287,21 +12300,21 @@ async function runWrapped(argv) {
|
|
|
12287
12300
|
const cwd = process.cwd();
|
|
12288
12301
|
const name = sessionNameFor(agentCmd, cwd, sessionOptedIn);
|
|
12289
12302
|
const { cmd, args } = tmuxRunArgv(agentCmd, name, cwd, sessionOptedIn ? ["JEFRI_EXPERIMENTAL_SESSION=1"] : []);
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12303
|
+
cp9.spawnSync(cmd, args, { stdio: "ignore" });
|
|
12304
|
+
cp9.spawnSync("tmux", ["set-option", "-t", name, "mouse", "on"], { stdio: "ignore" });
|
|
12305
|
+
cp9.spawnSync("tmux", ["set-option", "-t", name, "set-clipboard", "on"], { stdio: "ignore" });
|
|
12306
|
+
cp9.spawnSync("tmux", ["set-option", "-t", name, "history-limit", "50000"], { stdio: "ignore" });
|
|
12294
12307
|
for (const table of ["copy-mode", "copy-mode-vi"]) {
|
|
12295
|
-
|
|
12296
|
-
|
|
12308
|
+
cp9.spawnSync("tmux", ["bind-key", "-T", table, "WheelUpPane", "send-keys", "-N2", "-X", "scroll-up"], { stdio: "ignore" });
|
|
12309
|
+
cp9.spawnSync("tmux", ["bind-key", "-T", table, "WheelDownPane", "send-keys", "-N2", "-X", "scroll-down"], { stdio: "ignore" });
|
|
12297
12310
|
if (process.platform === "darwin")
|
|
12298
|
-
|
|
12311
|
+
cp9.spawnSync("tmux", ["bind-key", "-T", table, "MouseDragEnd1Pane", "send-keys", "-X", "copy-pipe-and-cancel", "pbcopy"], { stdio: "ignore" });
|
|
12299
12312
|
}
|
|
12300
12313
|
try {
|
|
12301
12314
|
const launcher = writePanelLauncher(`${selfCommand()} panel`);
|
|
12302
12315
|
const badge = writeBadgeScript(UNREAD_DIR, AGENTS_DIR);
|
|
12303
12316
|
for (const { cmd: c2, args: a2 } of tmuxPanelSetupArgv(name, launcher, badge))
|
|
12304
|
-
|
|
12317
|
+
cp9.spawnSync(c2, a2, { stdio: "ignore" });
|
|
12305
12318
|
process.stderr.write(` \u{1F4EC} Jefri panel: press Ctrl-b then j (or click the \u{1F4EC} badge) to read + reply.
|
|
12306
12319
|
|
|
12307
12320
|
`);
|
|
@@ -12333,7 +12346,7 @@ __export(panel_exports, {
|
|
|
12333
12346
|
runPanel: () => runPanel,
|
|
12334
12347
|
truncateToWidth: () => truncateToWidth
|
|
12335
12348
|
});
|
|
12336
|
-
import
|
|
12349
|
+
import readline3 from "node:readline/promises";
|
|
12337
12350
|
import { stdin as input, stdout as output } from "node:process";
|
|
12338
12351
|
function ago(at, now = Date.now()) {
|
|
12339
12352
|
const s = Math.max(0, Math.round((now - at) / 1e3));
|
|
@@ -12342,15 +12355,15 @@ function ago(at, now = Date.now()) {
|
|
|
12342
12355
|
if (s < 86400) return `${Math.round(s / 3600)}h`;
|
|
12343
12356
|
return `${Math.round(s / 86400)}d`;
|
|
12344
12357
|
}
|
|
12345
|
-
function charWidth(
|
|
12346
|
-
if (
|
|
12347
|
-
const wide =
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12358
|
+
function charWidth(cp10) {
|
|
12359
|
+
if (cp10 >= 768 && cp10 <= 879 || cp10 >= 6832 && cp10 <= 6911 || cp10 >= 7616 && cp10 <= 7679 || cp10 >= 8400 && cp10 <= 8432 || cp10 >= 65056 && cp10 <= 65071 || cp10 >= 8203 && cp10 <= 8205 || cp10 >= 65024 && cp10 <= 65039 || cp10 >= 917536 && cp10 <= 917631 || cp10 >= 917760 && cp10 <= 917999) return 0;
|
|
12360
|
+
const wide = cp10 >= 4352 && cp10 <= 4447 || // Hangul Jamo
|
|
12361
|
+
cp10 >= 11904 && cp10 <= 42191 && cp10 !== 12351 || // CJK radicals … Yi
|
|
12362
|
+
cp10 >= 44032 && cp10 <= 55203 || // Hangul syllables
|
|
12363
|
+
cp10 >= 63744 && cp10 <= 64255 || // CJK compatibility ideographs
|
|
12364
|
+
cp10 >= 65040 && cp10 <= 65049 || cp10 >= 65072 && cp10 <= 65135 || cp10 >= 65280 && cp10 <= 65376 || cp10 >= 65504 && cp10 <= 65510 || cp10 >= 127462 && cp10 <= 127487 || // regional indicators (flags)
|
|
12365
|
+
cp10 >= 127744 && cp10 <= 129791 || // every modern emoji block
|
|
12366
|
+
cp10 >= 131072 && cp10 <= 262141;
|
|
12354
12367
|
return wide ? 2 : 1;
|
|
12355
12368
|
}
|
|
12356
12369
|
function clusters(s) {
|
|
@@ -12476,7 +12489,7 @@ async function runPanel() {
|
|
|
12476
12489
|
dirty = true;
|
|
12477
12490
|
});
|
|
12478
12491
|
await session.subscribe();
|
|
12479
|
-
const rl =
|
|
12492
|
+
const rl = readline3.createInterface({ input, output });
|
|
12480
12493
|
const prompter = new Prompter(rl);
|
|
12481
12494
|
let messages = [];
|
|
12482
12495
|
const refresh = async () => {
|
|
@@ -38929,6 +38942,28 @@ var JefriClient = class _JefriClient {
|
|
|
38929
38942
|
announceHost(host) {
|
|
38930
38943
|
const h = (host ?? "").trim();
|
|
38931
38944
|
this.clientInfo = { ...this.clientInfo ?? { name: "jefri-sdk", version: "0" }, host: h };
|
|
38945
|
+
this.sendClientInfo();
|
|
38946
|
+
}
|
|
38947
|
+
/** Announce WHERE this connector runs — the folder and the machine.
|
|
38948
|
+
*
|
|
38949
|
+
* Separate from announceHost because the two are learned at different times
|
|
38950
|
+
* and by different means: the host app arrives from the MCP handshake, the
|
|
38951
|
+
* location is simply observed. Callers pass values they READ NOW
|
|
38952
|
+
* (process.cwd(), os.hostname()) — never a configured preference, which is
|
|
38953
|
+
* what made the old reported folder wrong. Omitted fields are left untouched
|
|
38954
|
+
* by the hub rather than cleared, so a caller that knows only one may send
|
|
38955
|
+
* only that one. */
|
|
38956
|
+
announceWhere(where) {
|
|
38957
|
+
const base = this.clientInfo ?? { name: "jefri-sdk", version: "0" };
|
|
38958
|
+
const next = { ...base };
|
|
38959
|
+
if (where.cwd !== void 0) next.cwd = (where.cwd ?? "").trim();
|
|
38960
|
+
if (where.machine !== void 0) next.machine = (where.machine ?? "").trim();
|
|
38961
|
+
this.clientInfo = next;
|
|
38962
|
+
this.sendClientInfo();
|
|
38963
|
+
}
|
|
38964
|
+
/** Push the current clientInfo if the socket is live. Reconnects re-send it
|
|
38965
|
+
* from the stored copy, so a failure here is not worth reporting. */
|
|
38966
|
+
sendClientInfo() {
|
|
38932
38967
|
try {
|
|
38933
38968
|
if (this.ws?.readyState === this.ws?.OPEN)
|
|
38934
38969
|
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
@@ -39590,31 +39625,90 @@ function configureAuto(opts) {
|
|
|
39590
39625
|
}
|
|
39591
39626
|
var watcher = null;
|
|
39592
39627
|
var watchDebounce = null;
|
|
39628
|
+
var watchRetry = null;
|
|
39629
|
+
var pollTimer = null;
|
|
39630
|
+
var pollSig = "";
|
|
39631
|
+
var watchAttempt = 0;
|
|
39632
|
+
var POLL_MS = 3e3;
|
|
39633
|
+
function applyConfigChange() {
|
|
39634
|
+
const wasEnabled = cfg.enabled;
|
|
39635
|
+
cfg = loadCfg();
|
|
39636
|
+
if (cfg.enabled !== wasEnabled) logLine(`config reloaded: enabled ${wasEnabled} \u2192 ${cfg.enabled}`);
|
|
39637
|
+
if (cfg.enabled) acquireResponderLock();
|
|
39638
|
+
else {
|
|
39639
|
+
releaseResponderLock();
|
|
39640
|
+
stopAllWork();
|
|
39641
|
+
}
|
|
39642
|
+
ensureIntervalTimer();
|
|
39643
|
+
}
|
|
39644
|
+
function configPath() {
|
|
39645
|
+
return np3.join(AUTO_DIR, `${safeUser(selfName)}.json`);
|
|
39646
|
+
}
|
|
39647
|
+
function configSignature() {
|
|
39648
|
+
try {
|
|
39649
|
+
const st = fs4.statSync(configPath());
|
|
39650
|
+
return `${st.mtimeMs}:${st.size}`;
|
|
39651
|
+
} catch {
|
|
39652
|
+
return "";
|
|
39653
|
+
}
|
|
39654
|
+
}
|
|
39655
|
+
function startPolling(why) {
|
|
39656
|
+
if (pollTimer) return;
|
|
39657
|
+
pollSig = configSignature();
|
|
39658
|
+
logLine(`config hot-reload switched to polling every ${POLL_MS / 1e3}s (${why}) \u2014 no descriptors needed`);
|
|
39659
|
+
pollTimer = setInterval(() => {
|
|
39660
|
+
const sig = configSignature();
|
|
39661
|
+
if (sig === pollSig) return;
|
|
39662
|
+
pollSig = sig;
|
|
39663
|
+
applyConfigChange();
|
|
39664
|
+
}, POLL_MS);
|
|
39665
|
+
pollTimer.unref?.();
|
|
39666
|
+
}
|
|
39667
|
+
function stopPolling() {
|
|
39668
|
+
if (!pollTimer) return;
|
|
39669
|
+
clearInterval(pollTimer);
|
|
39670
|
+
pollTimer = null;
|
|
39671
|
+
}
|
|
39593
39672
|
function watchConfig() {
|
|
39594
39673
|
try {
|
|
39595
39674
|
watcher?.close();
|
|
39596
39675
|
} catch {
|
|
39597
39676
|
}
|
|
39677
|
+
watcher = null;
|
|
39678
|
+
if (watchRetry) {
|
|
39679
|
+
clearTimeout(watchRetry);
|
|
39680
|
+
watchRetry = null;
|
|
39681
|
+
}
|
|
39682
|
+
const degrade = (why) => {
|
|
39683
|
+
startPolling(why);
|
|
39684
|
+
const delay = Math.min(3e4 * 2 ** watchAttempt++, 3e5);
|
|
39685
|
+
watchRetry = setTimeout(watchConfig, delay);
|
|
39686
|
+
watchRetry.unref?.();
|
|
39687
|
+
};
|
|
39598
39688
|
try {
|
|
39599
39689
|
fs4.mkdirSync(AUTO_DIR, { recursive: true });
|
|
39600
39690
|
const file = `${safeUser(selfName)}.json`;
|
|
39601
|
-
|
|
39691
|
+
const w = fs4.watch(AUTO_DIR, { persistent: false }, (_evt, name) => {
|
|
39602
39692
|
if (name && name !== file) return;
|
|
39603
39693
|
if (watchDebounce) clearTimeout(watchDebounce);
|
|
39604
|
-
watchDebounce = setTimeout(
|
|
39605
|
-
const wasEnabled = cfg.enabled;
|
|
39606
|
-
cfg = loadCfg();
|
|
39607
|
-
if (cfg.enabled !== wasEnabled) logLine(`config reloaded: enabled ${wasEnabled} \u2192 ${cfg.enabled}`);
|
|
39608
|
-
if (cfg.enabled) acquireResponderLock();
|
|
39609
|
-
else {
|
|
39610
|
-
releaseResponderLock();
|
|
39611
|
-
stopAllWork();
|
|
39612
|
-
}
|
|
39613
|
-
ensureIntervalTimer();
|
|
39614
|
-
}, 200);
|
|
39694
|
+
watchDebounce = setTimeout(applyConfigChange, 200);
|
|
39615
39695
|
});
|
|
39616
|
-
|
|
39617
|
-
|
|
39696
|
+
w.on("error", (err) => {
|
|
39697
|
+
logLine(`config watch failed (${err?.code ?? err?.message ?? err}) \u2014 falling back to polling, connector unaffected`);
|
|
39698
|
+
try {
|
|
39699
|
+
w.close();
|
|
39700
|
+
} catch {
|
|
39701
|
+
}
|
|
39702
|
+
if (watcher === w) watcher = null;
|
|
39703
|
+
degrade(String(err?.code ?? "watch error"));
|
|
39704
|
+
});
|
|
39705
|
+
watcher = w;
|
|
39706
|
+
watchAttempt = 0;
|
|
39707
|
+
stopPolling();
|
|
39708
|
+
w.unref?.();
|
|
39709
|
+
} catch (e) {
|
|
39710
|
+
logLine(`config watch unavailable (${e?.code ?? e?.message ?? e}) \u2014 using polling instead`);
|
|
39711
|
+
degrade(String(e?.code ?? "watch unavailable"));
|
|
39618
39712
|
}
|
|
39619
39713
|
}
|
|
39620
39714
|
var LOCK_STALE_MS = 9e4;
|
|
@@ -40694,7 +40788,24 @@ function waitFor(c2, event, match, timeoutMs = 4e3) {
|
|
|
40694
40788
|
}, timeoutMs);
|
|
40695
40789
|
});
|
|
40696
40790
|
}
|
|
40697
|
-
var
|
|
40791
|
+
var upgradeNotice = null;
|
|
40792
|
+
var upgradeShownAt = 0;
|
|
40793
|
+
var UPGRADE_REPEAT_MS = 24 * 60 * 60 * 1e3;
|
|
40794
|
+
function setUpgradeNotice(text5) {
|
|
40795
|
+
upgradeNotice = text5;
|
|
40796
|
+
}
|
|
40797
|
+
function takeUpgradeNotice(now = Date.now()) {
|
|
40798
|
+
if (!upgradeNotice) return null;
|
|
40799
|
+
if (upgradeShownAt && now - upgradeShownAt < UPGRADE_REPEAT_MS) return null;
|
|
40800
|
+
upgradeShownAt = now;
|
|
40801
|
+
return upgradeNotice;
|
|
40802
|
+
}
|
|
40803
|
+
var ok3 = (text5, now = Date.now()) => {
|
|
40804
|
+
const notice = takeUpgradeNotice(now);
|
|
40805
|
+
return { content: [{ type: "text", text: notice ? `${text5}
|
|
40806
|
+
|
|
40807
|
+
${notice}` : text5 }] };
|
|
40808
|
+
};
|
|
40698
40809
|
var fail = (text5) => ({ content: [{ type: "text", text: text5 }], isError: true });
|
|
40699
40810
|
var WATERMARK_FILE = np4.join(os4.homedir(), ".jefri", "inbox.json");
|
|
40700
40811
|
var inboxWatermark = new Map(
|
|
@@ -41856,6 +41967,9 @@ function attachInbox(c2, inbox2, self, onIncoming) {
|
|
|
41856
41967
|
c2.on("file_received", capture);
|
|
41857
41968
|
}
|
|
41858
41969
|
|
|
41970
|
+
// src/index.ts
|
|
41971
|
+
init_control_proto();
|
|
41972
|
+
|
|
41859
41973
|
// src/control.ts
|
|
41860
41974
|
init_control_proto();
|
|
41861
41975
|
init_registry();
|
|
@@ -41883,6 +41997,8 @@ async function startControlServer(opts) {
|
|
|
41883
41997
|
owner: opts.owner ?? null,
|
|
41884
41998
|
host: opts.getHost(),
|
|
41885
41999
|
workdir: opts.workdir,
|
|
42000
|
+
cwd: opts.cwd ?? null,
|
|
42001
|
+
machine: opts.machine ?? null,
|
|
41886
42002
|
server: opts.server,
|
|
41887
42003
|
online: opts.isOnline(),
|
|
41888
42004
|
unread: currentTally().total,
|
|
@@ -41944,6 +42060,19 @@ async function startControlServer(opts) {
|
|
|
41944
42060
|
case "subscribe":
|
|
41945
42061
|
c2.subscribed = true;
|
|
41946
42062
|
return reply(c2, okRes(id, { subscribed: true }));
|
|
42063
|
+
case "history": {
|
|
42064
|
+
if (!opts.fetchHistory) return fail3(c2, id, "unknown_method", "history is not available from this endpoint");
|
|
42065
|
+
const withUser = typeof params.with === "string" ? params.with : void 0;
|
|
42066
|
+
const groupId = typeof params.groupId === "string" ? params.groupId : void 0;
|
|
42067
|
+
if (!withUser && !groupId) return fail3(c2, id, "bad_request", "history needs `with` (a username) or `groupId`");
|
|
42068
|
+
const limit = Math.min(Math.max(Number(params.limit) || 50, 1), 200);
|
|
42069
|
+
try {
|
|
42070
|
+
const messages2 = await opts.fetchHistory({ with: withUser, groupId, limit });
|
|
42071
|
+
return reply(c2, okRes(id, { messages: messages2 }));
|
|
42072
|
+
} catch (e) {
|
|
42073
|
+
return fail3(c2, id, "internal", `history failed: ${e?.message ?? e}`);
|
|
42074
|
+
}
|
|
42075
|
+
}
|
|
41947
42076
|
case "inbox": {
|
|
41948
42077
|
const limit = Math.min(Math.max(Number(params.limit) || 50, 1), MAX_STORED);
|
|
41949
42078
|
return reply(c2, okRes(id, { messages: messages.slice(-limit).reverse() }));
|
|
@@ -42059,7 +42188,14 @@ async function startControlServer(opts) {
|
|
|
42059
42188
|
pid: process.pid,
|
|
42060
42189
|
server: opts.server,
|
|
42061
42190
|
host: opts.getHost(),
|
|
42191
|
+
// Both, and clearly distinct. `workdir` is the brain's configured folder;
|
|
42192
|
+
// `cwd` is where this process runs. The descriptor previously carried only
|
|
42193
|
+
// `workdir` under a name every reader took to mean "where it runs" — which
|
|
42194
|
+
// is how a stale setting ended up being handed out as the answer to "where
|
|
42195
|
+
// do I go to restart this?".
|
|
42062
42196
|
workdir: opts.workdir,
|
|
42197
|
+
cwd: opts.cwd ?? null,
|
|
42198
|
+
machine: opts.machine ?? null,
|
|
42063
42199
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
42064
42200
|
});
|
|
42065
42201
|
writeUnreadFor(self, 0);
|
|
@@ -42139,9 +42275,150 @@ async function startControlServer(opts) {
|
|
|
42139
42275
|
init_popup();
|
|
42140
42276
|
|
|
42141
42277
|
// src/doctor.ts
|
|
42142
|
-
import
|
|
42278
|
+
import cp8 from "node:child_process";
|
|
42143
42279
|
init_version();
|
|
42144
42280
|
init_banner();
|
|
42281
|
+
|
|
42282
|
+
// src/fdcheck.ts
|
|
42283
|
+
import cp6 from "node:child_process";
|
|
42284
|
+
function labelFor(command) {
|
|
42285
|
+
if (/tsx[/\\]dist[/\\]cli\.mjs\s+watch/.test(command)) return "tsx watch dev server";
|
|
42286
|
+
if (/next(-server)?\b.*\bdev\b/.test(command) || /next dev/.test(command)) return "next dev server";
|
|
42287
|
+
if (/nodemon/.test(command)) return "nodemon";
|
|
42288
|
+
if (/webpack|vite/.test(command)) return "bundler dev server";
|
|
42289
|
+
if (/jefrichat-mcp/.test(command)) return "jefri connector";
|
|
42290
|
+
const bin = command.split(/\s+/)[1] ?? command.split(/\s+/)[0] ?? command;
|
|
42291
|
+
return bin.split("/").pop() || "process";
|
|
42292
|
+
}
|
|
42293
|
+
function fdAdvice(hogs, opts) {
|
|
42294
|
+
if (!hogs.length) return null;
|
|
42295
|
+
const total = hogs.reduce((n, h) => n + h.fds, 0);
|
|
42296
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
42297
|
+
for (const h of hogs) {
|
|
42298
|
+
const cur = byLabel.get(h.label) ?? { count: 0, fds: 0 };
|
|
42299
|
+
byLabel.set(h.label, { count: cur.count + 1, fds: cur.fds + h.fds });
|
|
42300
|
+
}
|
|
42301
|
+
const worst = [...byLabel.entries()].sort((a, b) => b[1].fds - a[1].fds)[0];
|
|
42302
|
+
const [label, agg] = worst;
|
|
42303
|
+
const manyDuplicates = agg.count >= 4;
|
|
42304
|
+
const heavy = total > 2e4 || agg.fds > opts.softLimit * 4;
|
|
42305
|
+
if (!manyDuplicates && !heavy) return null;
|
|
42306
|
+
const plural = agg.count === 1 ? "" : "es";
|
|
42307
|
+
return `${agg.count} "${label}" process${plural} are holding ~${agg.fds.toLocaleString()} open files. File watching (and eventually new connections) can fail with EMFILE while they run.`;
|
|
42308
|
+
}
|
|
42309
|
+
function probeFdHogs(limit = 40) {
|
|
42310
|
+
if (process.platform === "win32") return [];
|
|
42311
|
+
try {
|
|
42312
|
+
const out = cp6.execSync("lsof -n -P 2>/dev/null | awk 'NR>1 {print $2}' | sort | uniq -c | sort -rn | head -" + limit, {
|
|
42313
|
+
encoding: "utf8",
|
|
42314
|
+
timeout: 8e3,
|
|
42315
|
+
maxBuffer: 8 * 1024 * 1024
|
|
42316
|
+
});
|
|
42317
|
+
const hogs = [];
|
|
42318
|
+
for (const line of out.split("\n")) {
|
|
42319
|
+
const m = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
|
|
42320
|
+
if (!m) continue;
|
|
42321
|
+
const fds = Number(m[1]);
|
|
42322
|
+
const pid = Number(m[2]);
|
|
42323
|
+
if (fds < 500) continue;
|
|
42324
|
+
let command = "";
|
|
42325
|
+
try {
|
|
42326
|
+
command = cp6.execSync(`ps -p ${pid} -o command=`, { encoding: "utf8", timeout: 2e3 }).trim();
|
|
42327
|
+
} catch {
|
|
42328
|
+
continue;
|
|
42329
|
+
}
|
|
42330
|
+
if (!command) continue;
|
|
42331
|
+
hogs.push({ pid, fds, label: labelFor(command) });
|
|
42332
|
+
}
|
|
42333
|
+
return hogs;
|
|
42334
|
+
} catch {
|
|
42335
|
+
return [];
|
|
42336
|
+
}
|
|
42337
|
+
}
|
|
42338
|
+
function softFdLimit() {
|
|
42339
|
+
try {
|
|
42340
|
+
const n = Number(cp6.execSync("ulimit -n", { encoding: "utf8", shell: "/bin/sh", timeout: 2e3 }).trim());
|
|
42341
|
+
return Number.isFinite(n) && n > 0 ? n : 256;
|
|
42342
|
+
} catch {
|
|
42343
|
+
return 256;
|
|
42344
|
+
}
|
|
42345
|
+
}
|
|
42346
|
+
|
|
42347
|
+
// src/setup.ts
|
|
42348
|
+
init_which();
|
|
42349
|
+
import cp7 from "node:child_process";
|
|
42350
|
+
import readline from "node:readline";
|
|
42351
|
+
var LINUX_PKG = { "terminal-notifier": "" };
|
|
42352
|
+
function resolveInstall(tool, platform, has = isOnPath) {
|
|
42353
|
+
if (has(tool)) return { kind: "present", command: "" };
|
|
42354
|
+
if (platform === "darwin") {
|
|
42355
|
+
return has("brew") ? { kind: "brew", command: `brew install ${tool}` } : { kind: "brew-missing", command: `install Homebrew first (https://brew.sh), then: brew install ${tool}` };
|
|
42356
|
+
}
|
|
42357
|
+
if (LINUX_PKG[tool] === "") {
|
|
42358
|
+
return { kind: "windows", command: `${tool} is macOS-only \u2014 notifications on this platform are informational` };
|
|
42359
|
+
}
|
|
42360
|
+
if (platform === "win32") {
|
|
42361
|
+
return { kind: "windows", command: `${tool} has no native Windows build \u2014 use WSL, or skip it` };
|
|
42362
|
+
}
|
|
42363
|
+
const mgr = ["apt-get", "apt", "dnf", "yum", "pacman", "zypper", "apk"].find(has);
|
|
42364
|
+
const command = mgr === "pacman" ? `sudo pacman -S ${tool}` : mgr === "apk" ? `sudo apk add ${tool}` : mgr ? `sudo ${mgr} install ${tool}` : `install ${tool} with your distro's package manager`;
|
|
42365
|
+
return { kind: "linux-manual", command };
|
|
42366
|
+
}
|
|
42367
|
+
async function promptYesNoDefaultYes(question) {
|
|
42368
|
+
if (!process.stdin.isTTY) return false;
|
|
42369
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
42370
|
+
try {
|
|
42371
|
+
const ans = (await new Promise((res) => rl.question(question, res))).trim().toLowerCase();
|
|
42372
|
+
return ans === "" || ans === "y" || ans === "yes";
|
|
42373
|
+
} finally {
|
|
42374
|
+
rl.close();
|
|
42375
|
+
}
|
|
42376
|
+
}
|
|
42377
|
+
async function ensureToolInteractive(opts) {
|
|
42378
|
+
const out = opts.out ?? process.stderr;
|
|
42379
|
+
const plan = resolveInstall(opts.tool, process.platform);
|
|
42380
|
+
if (plan.kind === "present") return true;
|
|
42381
|
+
out.write(`
|
|
42382
|
+
\u{1F427} ${opts.why}
|
|
42383
|
+
`);
|
|
42384
|
+
if (plan.kind !== "brew") {
|
|
42385
|
+
out.write(` ${plan.command}
|
|
42386
|
+
`);
|
|
42387
|
+
if (opts.thenWhat) out.write(` ${opts.thenWhat}
|
|
42388
|
+
`);
|
|
42389
|
+
return false;
|
|
42390
|
+
}
|
|
42391
|
+
const yes = await promptYesNoDefaultYes(` Install ${opts.tool} now with Homebrew? (no password, no sudo) [Y/n] `);
|
|
42392
|
+
if (!yes) {
|
|
42393
|
+
out.write(` No problem \u2014 any time: ${plan.command}
|
|
42394
|
+
`);
|
|
42395
|
+
return false;
|
|
42396
|
+
}
|
|
42397
|
+
out.write(` Installing ${opts.tool}\u2026
|
|
42398
|
+
`);
|
|
42399
|
+
try {
|
|
42400
|
+
cp7.execFileSync("brew", ["install", opts.tool], { stdio: "inherit" });
|
|
42401
|
+
} catch {
|
|
42402
|
+
out.write(` \u26A0\uFE0F '${plan.command}' didn't finish \u2014 run it yourself, then try again.
|
|
42403
|
+
`);
|
|
42404
|
+
return false;
|
|
42405
|
+
}
|
|
42406
|
+
if (isOnPath(opts.tool)) {
|
|
42407
|
+
out.write(` \u2705 ${opts.tool} installed.
|
|
42408
|
+
`);
|
|
42409
|
+
return true;
|
|
42410
|
+
}
|
|
42411
|
+
out.write(` \u26A0\uFE0F ${opts.tool} isn't on PATH yet \u2014 open a new terminal and re-run.
|
|
42412
|
+
`);
|
|
42413
|
+
return false;
|
|
42414
|
+
}
|
|
42415
|
+
var WHY = {
|
|
42416
|
+
"terminal-notifier": 'Clicking a Jefri notification can open a reply box, so you answer without\n going through your agent. macOS needs "terminal-notifier" for that \u2014 without\n it the banner still appears, but clicking it does nothing.',
|
|
42417
|
+
tmux: 'Autonomous "session" mode types incoming messages straight into your LIVE\n agent window so you can watch it work, and gives the reply popup somewhere\n nicer to open. Both need "tmux", a small standard terminal tool.'
|
|
42418
|
+
};
|
|
42419
|
+
|
|
42420
|
+
// src/doctor.ts
|
|
42421
|
+
init_which();
|
|
42145
42422
|
init_popup();
|
|
42146
42423
|
import fs10 from "node:fs";
|
|
42147
42424
|
var T = !!process.stdout.isTTY;
|
|
@@ -42173,7 +42450,7 @@ function mask(t) {
|
|
|
42173
42450
|
}
|
|
42174
42451
|
function hasCli(cmd) {
|
|
42175
42452
|
try {
|
|
42176
|
-
const r =
|
|
42453
|
+
const r = cp8.spawnSync(cmd, ["--version"], {
|
|
42177
42454
|
encoding: "utf8",
|
|
42178
42455
|
timeout: 5e3,
|
|
42179
42456
|
shell: process.platform === "win32"
|
|
@@ -42267,6 +42544,18 @@ async function runDoctor() {
|
|
|
42267
42544
|
const runMode2 = auto.mode === "session" ? auto.sessionReady ? "session (types into your live jefri-managed tmux session)" : "session (\u26A0\uFE0F not a jefri-managed tmux session \u2014 falls back to headless; launch via `jefrichat-mcp run <agent>`)" : auto.mode === "interval" ? `interval (batches every ${auto.intervalMinutes}m)` : "headless (one-shot per message)";
|
|
42268
42545
|
info(`Autonomous mode is ON \u2014 ${runMode2}, brain "${auto.brain}" in ${auto.workdir} (jefri_autonomous to change)`);
|
|
42269
42546
|
}
|
|
42547
|
+
try {
|
|
42548
|
+
const hogs = probeFdHogs();
|
|
42549
|
+
const advice = fdAdvice(hogs, { softLimit: softFdLimit() });
|
|
42550
|
+
if (advice) {
|
|
42551
|
+
const worst = hogs.sort((a, b) => b.fds - a.fds)[0];
|
|
42552
|
+
warn(
|
|
42553
|
+
advice,
|
|
42554
|
+
`biggest: pid ${worst.pid} (${worst.fds.toLocaleString()} files). List them with: lsof -n -P | awk '{print $2}' | sort | uniq -c | sort -rn | head`
|
|
42555
|
+
);
|
|
42556
|
+
}
|
|
42557
|
+
} catch {
|
|
42558
|
+
}
|
|
42270
42559
|
console.log("");
|
|
42271
42560
|
console.log(
|
|
42272
42561
|
` ${C.b}\u{1F4EC} ${panelInvocation(hasCli)}${C.x} ${C.d}\u2014 read your messages and reply WITHOUT going through your agent${C.x}`
|
|
@@ -42280,6 +42569,22 @@ async function runDoctor() {
|
|
|
42280
42569
|
if (fs10.existsSync(app))
|
|
42281
42570
|
console.log(` ${C.d}no terminal? open "Jefri Chat" from Spotlight (or drag ${app} to your Dock)${C.x}`);
|
|
42282
42571
|
}
|
|
42572
|
+
if (process.stdin.isTTY) {
|
|
42573
|
+
if (macNeedsTerminalNotifier && getPrefs().enabled) {
|
|
42574
|
+
await ensureToolInteractive({
|
|
42575
|
+
tool: "terminal-notifier",
|
|
42576
|
+
why: WHY["terminal-notifier"],
|
|
42577
|
+
thenWhat: `Then re-run: ${hasCli("jefri") ? "jefri" : "jefrichat-mcp"} doctor`
|
|
42578
|
+
});
|
|
42579
|
+
}
|
|
42580
|
+
if (auto.enabled && auto.mode === "session" && !isOnPath("tmux")) {
|
|
42581
|
+
await ensureToolInteractive({
|
|
42582
|
+
tool: "tmux",
|
|
42583
|
+
why: WHY.tmux,
|
|
42584
|
+
thenWhat: `Then launch with: JEFRI_EXPERIMENTAL_SESSION=1 ${hasCli("jefri") ? "jefri" : "jefrichat-mcp"} run <agent>`
|
|
42585
|
+
});
|
|
42586
|
+
}
|
|
42587
|
+
}
|
|
42283
42588
|
console.log("");
|
|
42284
42589
|
if (problems === 0 && warnings === 0) {
|
|
42285
42590
|
console.log(`${C.g}${C.b}All good \u2014 you're ready to chat.${C.x}
|
|
@@ -42337,7 +42642,12 @@ function ensureClient() {
|
|
|
42337
42642
|
if (!clientPromise) {
|
|
42338
42643
|
clientPromise = (async () => {
|
|
42339
42644
|
const known = TOKEN ?? readTokenCache()[cacheKey];
|
|
42340
|
-
const clientInfo = {
|
|
42645
|
+
const clientInfo = {
|
|
42646
|
+
name: "jefrichat-mcp",
|
|
42647
|
+
version: connectorVersion(),
|
|
42648
|
+
cwd: process.cwd(),
|
|
42649
|
+
machine: os7.hostname()
|
|
42650
|
+
};
|
|
42341
42651
|
const provision = () => JefriClient.connect({
|
|
42342
42652
|
server: SERVER,
|
|
42343
42653
|
username: USERNAME,
|
|
@@ -42410,6 +42720,8 @@ function ensureClient() {
|
|
|
42410
42720
|
owner: c2.identity?.owner,
|
|
42411
42721
|
server: SERVER,
|
|
42412
42722
|
workdir: getAuto().workdir,
|
|
42723
|
+
cwd: process.cwd(),
|
|
42724
|
+
machine: os7.hostname(),
|
|
42413
42725
|
getHost: () => detectedHost,
|
|
42414
42726
|
isOnline: () => c2.online,
|
|
42415
42727
|
isAutonomous: () => getAuto().enabled,
|
|
@@ -42418,6 +42730,31 @@ function ensureClient() {
|
|
|
42418
42730
|
return { label: b.resolved, ready: b.ready };
|
|
42419
42731
|
},
|
|
42420
42732
|
sendText: sendAs,
|
|
42733
|
+
// Read a conversation from the HUB, over the socket this process
|
|
42734
|
+
// already holds. The panel's own inbox only has what arrived while
|
|
42735
|
+
// this connector was running, so a freshly-started UI shows nothing
|
|
42736
|
+
// for a conversation that has been going for weeks — which reads as
|
|
42737
|
+
// broken rather than as empty.
|
|
42738
|
+
fetchHistory: async ({ with: other, groupId, limit }) => {
|
|
42739
|
+
const convId = groupId ? groupConversationId(groupId) : dmConversationId(self, other);
|
|
42740
|
+
const p = waitFor(c2, "history", (e) => e.conversationId === convId);
|
|
42741
|
+
c2.history(convId);
|
|
42742
|
+
const res = await p;
|
|
42743
|
+
const raw = (res?.messages ?? []).slice(-limit);
|
|
42744
|
+
return raw.map((m) => ({
|
|
42745
|
+
id: String(m.id ?? ""),
|
|
42746
|
+
// Raw username for routing, sanitized copy for display — the same
|
|
42747
|
+
// split the panel makes, for the same reason: a name can carry a
|
|
42748
|
+
// bidi override that would reorder the line around it.
|
|
42749
|
+
from: String(m.senderUsername ?? ""),
|
|
42750
|
+
fromDisplay: sanitizeForDisplay(String(m.senderUsername ?? "")),
|
|
42751
|
+
text: m.kind === "file" ? `\u{1F4CE} ${m.fileName ?? "file"}` : String(m.content ?? ""),
|
|
42752
|
+
at: String(m.createdAt ?? ""),
|
|
42753
|
+
mine: String(m.senderUsername ?? "") === self,
|
|
42754
|
+
kind: m.kind === "file" ? "file" : "text",
|
|
42755
|
+
fileName: m.fileName ?? null
|
|
42756
|
+
}));
|
|
42757
|
+
},
|
|
42421
42758
|
handleWithBrain: async (m) => {
|
|
42422
42759
|
const text5 = await runOnceForMessage({
|
|
42423
42760
|
content: m.text,
|
|
@@ -42601,11 +42938,29 @@ async function main() {
|
|
|
42601
42938
|
};
|
|
42602
42939
|
process.stdin.once("end", shutdownOnHostClose);
|
|
42603
42940
|
process.stdin.once("close", shutdownOnHostClose);
|
|
42941
|
+
process.on("uncaughtException", (err) => {
|
|
42942
|
+
const code3 = err?.code;
|
|
42943
|
+
if (code3 === "EMFILE" || code3 === "ENFILE" || code3 === "EAGAIN") {
|
|
42944
|
+
log(
|
|
42945
|
+
`WARNING: ${code3} \u2014 this machine is out of file descriptors. Something else is holding them (check for abandoned \`tsx watch\`/dev-server processes). Continuing in degraded mode; file watching may not work until pressure drops.`
|
|
42946
|
+
);
|
|
42947
|
+
return;
|
|
42948
|
+
}
|
|
42949
|
+
log("fatal (uncaught):", err?.stack ?? err);
|
|
42950
|
+
process.exit(1);
|
|
42951
|
+
});
|
|
42952
|
+
process.on("unhandledRejection", (reason) => {
|
|
42953
|
+
log("unhandled rejection:", reason?.stack ?? reason);
|
|
42954
|
+
});
|
|
42604
42955
|
void checkForUpdate().then((u) => {
|
|
42605
|
-
if (u?.outdated)
|
|
42956
|
+
if (u?.outdated) {
|
|
42957
|
+
setUpgradeNotice(
|
|
42958
|
+
`\u26A0\uFE0F Jefri Chat connector ${u.current} is out of date (latest ${u.latest}). Restart this agent to upgrade \u2014 a running connector keeps the build it started with. If it was started with npx, that is enough; if installed globally, run: npm update -g jefrichat-mcp`
|
|
42959
|
+
);
|
|
42606
42960
|
note(
|
|
42607
42961
|
`\u2191 update available: jefrichat-mcp ${u.current} \u2192 ${u.latest}. Reconnect this agent (npx jefrichat-mcp@latest, or 'npm update -g jefrichat-mcp@latest' if globally installed) to get it.`
|
|
42608
42962
|
);
|
|
42963
|
+
}
|
|
42609
42964
|
}).catch(() => {
|
|
42610
42965
|
});
|
|
42611
42966
|
}
|
package/package.json
CHANGED