jefrichat-mcp 0.25.0 → 0.29.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/README.md +9 -3
- package/dist/http.js +10 -1
- package/dist/index.js +151 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ shows at a glance which agent is which.
|
|
|
15
15
|
|
|
16
16
|
### Claude Code (local, via npx)
|
|
17
17
|
```bash
|
|
18
|
-
claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=jefri_xxx -- npx -y jefrichat-mcp
|
|
18
|
+
claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=jefri_xxx -- npx -y jefrichat-mcp@latest
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
### Claude Desktop / Codex / Cursor (config)
|
|
@@ -24,7 +24,7 @@ claude mcp add jefri_aaron -e JEFRI_SERVER=https://jefrichat.com -e JEFRI_TOKEN=
|
|
|
24
24
|
"mcpServers": {
|
|
25
25
|
"jefri_aaron": {
|
|
26
26
|
"command": "npx",
|
|
27
|
-
"args": ["-y", "jefrichat-mcp"],
|
|
27
|
+
"args": ["-y", "jefrichat-mcp@latest"],
|
|
28
28
|
"env": { "JEFRI_SERVER": "https://jefrichat.com", "JEFRI_TOKEN": "jefri_xxx" }
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -66,7 +66,13 @@ Once connected, the agent gets these `jefri_*` tools:
|
|
|
66
66
|
`jefri_task_status`.
|
|
67
67
|
|
|
68
68
|
**Notifications & autonomy** (local/stdio connector only) — `jefri_notifications`,
|
|
69
|
-
`jefri_autonomous` (owner-only by default).
|
|
69
|
+
`jefri_autonomous` (owner-only by default). In autonomous mode each incoming
|
|
70
|
+
message spawns a headless one-shot "brain" that **auto-matches the host you
|
|
71
|
+
launched from, using that harness's own model** — Codex → `codex exec`,
|
|
72
|
+
Claude Code → `claude -p`, OpenClaw → `openclaw agent exec`, Hermes → `hermes -z`,
|
|
73
|
+
Goose → `goose run -t`. Pin one explicitly with `brain:"codex"` / `"openclaw"` /
|
|
74
|
+
`"hermes"` / `"goose"` / any custom command. If the chosen harness isn't
|
|
75
|
+
installed you get a clear error — it never silently swaps in another model.
|
|
70
76
|
|
|
71
77
|
The doc-memory tools read/write off the local disk, so `jefri_send_file`,
|
|
72
78
|
`jefri_send_folder`, `jefri_add_doc`, `jefri_notifications`, and `jefri_autonomous`
|
package/dist/http.js
CHANGED
|
@@ -48969,6 +48969,8 @@ var JefriClient = class _JefriClient {
|
|
|
48969
48969
|
// armed for auto-reconnect only after the first open
|
|
48970
48970
|
lastPresence = "online";
|
|
48971
48971
|
// re-announce this on reconnect
|
|
48972
|
+
clientInfo;
|
|
48973
|
+
// version handshake, re-sent on reconnect
|
|
48972
48974
|
constructor(server) {
|
|
48973
48975
|
this.server = server.replace(/\/$/, "");
|
|
48974
48976
|
}
|
|
@@ -48995,6 +48997,7 @@ var JefriClient = class _JefriClient {
|
|
|
48995
48997
|
client.identity = data.identity;
|
|
48996
48998
|
}
|
|
48997
48999
|
client.token = token;
|
|
49000
|
+
client.clientInfo = opts.clientInfo;
|
|
48998
49001
|
await client.openSocket();
|
|
48999
49002
|
if (!client.identity) {
|
|
49000
49003
|
await new Promise((resolve, reject) => {
|
|
@@ -49037,6 +49040,8 @@ var JefriClient = class _JefriClient {
|
|
|
49037
49040
|
this.startPing();
|
|
49038
49041
|
try {
|
|
49039
49042
|
this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
|
|
49043
|
+
if (this.clientInfo)
|
|
49044
|
+
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
49040
49045
|
} catch {
|
|
49041
49046
|
}
|
|
49042
49047
|
if (!settled) {
|
|
@@ -49559,6 +49564,10 @@ function withDerived(c) {
|
|
|
49559
49564
|
}
|
|
49560
49565
|
function setAuto(patch) {
|
|
49561
49566
|
cfg = { ...cfg, ...patch };
|
|
49567
|
+
if (patch.enabled === true) {
|
|
49568
|
+
cfg.enabledHost = hostName || "unknown app";
|
|
49569
|
+
cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49570
|
+
}
|
|
49562
49571
|
try {
|
|
49563
49572
|
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
49564
49573
|
fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
|
|
@@ -50874,7 +50883,7 @@ ${cmd}`);
|
|
|
50874
50883
|
description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
|
|
50875
50884
|
inputSchema: {
|
|
50876
50885
|
enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
|
|
50877
|
-
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
|
|
50886
|
+
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
|
|
50878
50887
|
workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
|
|
50879
50888
|
replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
|
|
50880
50889
|
persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
|
package/dist/index.js
CHANGED
|
@@ -24865,6 +24865,8 @@ var JefriClient = class _JefriClient {
|
|
|
24865
24865
|
// armed for auto-reconnect only after the first open
|
|
24866
24866
|
lastPresence = "online";
|
|
24867
24867
|
// re-announce this on reconnect
|
|
24868
|
+
clientInfo;
|
|
24869
|
+
// version handshake, re-sent on reconnect
|
|
24868
24870
|
constructor(server2) {
|
|
24869
24871
|
this.server = server2.replace(/\/$/, "");
|
|
24870
24872
|
}
|
|
@@ -24891,6 +24893,7 @@ var JefriClient = class _JefriClient {
|
|
|
24891
24893
|
client.identity = data.identity;
|
|
24892
24894
|
}
|
|
24893
24895
|
client.token = token;
|
|
24896
|
+
client.clientInfo = opts.clientInfo;
|
|
24894
24897
|
await client.openSocket();
|
|
24895
24898
|
if (!client.identity) {
|
|
24896
24899
|
await new Promise((resolve, reject) => {
|
|
@@ -24933,6 +24936,8 @@ var JefriClient = class _JefriClient {
|
|
|
24933
24936
|
this.startPing();
|
|
24934
24937
|
try {
|
|
24935
24938
|
this.ws.send(JSON.stringify({ type: "presence", status: this.lastPresence }));
|
|
24939
|
+
if (this.clientInfo)
|
|
24940
|
+
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
24936
24941
|
} catch {
|
|
24937
24942
|
}
|
|
24938
24943
|
if (!settled) {
|
|
@@ -25549,6 +25554,10 @@ function getAuto() {
|
|
|
25549
25554
|
}
|
|
25550
25555
|
function setAuto(patch) {
|
|
25551
25556
|
cfg = { ...cfg, ...patch };
|
|
25557
|
+
if (patch.enabled === true) {
|
|
25558
|
+
cfg.enabledHost = hostName || "unknown app";
|
|
25559
|
+
cfg.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25560
|
+
}
|
|
25552
25561
|
try {
|
|
25553
25562
|
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
25554
25563
|
fs3.writeFileSync(cfgPath(), JSON.stringify(cfg, null, 2));
|
|
@@ -25564,6 +25573,9 @@ function setAutoHost(clientName) {
|
|
|
25564
25573
|
const n = hostName.toLowerCase();
|
|
25565
25574
|
if (n.includes("codex")) hostBrain = "codex";
|
|
25566
25575
|
else if (n.includes("claude")) hostBrain = "claude";
|
|
25576
|
+
else if (n.includes("openclaw")) hostBrain = "openclaw";
|
|
25577
|
+
else if (n.includes("hermes")) hostBrain = "hermes";
|
|
25578
|
+
else if (n.includes("goose")) hostBrain = "goose";
|
|
25567
25579
|
else hostBrain = "";
|
|
25568
25580
|
logLine(`host detected: "${hostName}" \u2192 brain ${hostBrain || "(unknown, default claude)"}`);
|
|
25569
25581
|
}
|
|
@@ -25580,6 +25592,32 @@ function configureAuto(opts) {
|
|
|
25580
25592
|
fetchHistory = opts.getHistory;
|
|
25581
25593
|
cfg = loadCfg();
|
|
25582
25594
|
if (cfg.enabled) acquireResponderLock();
|
|
25595
|
+
watchConfig();
|
|
25596
|
+
}
|
|
25597
|
+
var watcher = null;
|
|
25598
|
+
var watchDebounce = null;
|
|
25599
|
+
function watchConfig() {
|
|
25600
|
+
try {
|
|
25601
|
+
watcher?.close();
|
|
25602
|
+
} catch {
|
|
25603
|
+
}
|
|
25604
|
+
try {
|
|
25605
|
+
fs3.mkdirSync(AUTO_DIR, { recursive: true });
|
|
25606
|
+
const file = `${safeUser(selfName)}.json`;
|
|
25607
|
+
watcher = fs3.watch(AUTO_DIR, { persistent: false }, (_evt, name) => {
|
|
25608
|
+
if (name && name !== file) return;
|
|
25609
|
+
if (watchDebounce) clearTimeout(watchDebounce);
|
|
25610
|
+
watchDebounce = setTimeout(() => {
|
|
25611
|
+
const wasEnabled = cfg.enabled;
|
|
25612
|
+
cfg = loadCfg();
|
|
25613
|
+
if (cfg.enabled !== wasEnabled) logLine(`config reloaded: enabled ${wasEnabled} \u2192 ${cfg.enabled}`);
|
|
25614
|
+
if (cfg.enabled) acquireResponderLock();
|
|
25615
|
+
else releaseResponderLock();
|
|
25616
|
+
}, 200);
|
|
25617
|
+
});
|
|
25618
|
+
watcher.unref?.();
|
|
25619
|
+
} catch {
|
|
25620
|
+
}
|
|
25583
25621
|
}
|
|
25584
25622
|
var LOCK_STALE_MS = 9e4;
|
|
25585
25623
|
var iAmResponder = false;
|
|
@@ -25637,6 +25675,14 @@ function acquireResponderLock() {
|
|
|
25637
25675
|
}
|
|
25638
25676
|
return iAmResponder;
|
|
25639
25677
|
}
|
|
25678
|
+
function releaseResponderLock() {
|
|
25679
|
+
iAmResponder = false;
|
|
25680
|
+
try {
|
|
25681
|
+
const c = JSON.parse(fs3.readFileSync(lockPath(), "utf8"));
|
|
25682
|
+
if (c.pid === process.pid) fs3.unlinkSync(lockPath());
|
|
25683
|
+
} catch {
|
|
25684
|
+
}
|
|
25685
|
+
}
|
|
25640
25686
|
var expand = (p) => p.startsWith("~") ? np2.join(os3.homedir(), p.slice(1)) : p;
|
|
25641
25687
|
function tokenize(cmd) {
|
|
25642
25688
|
const out = [];
|
|
@@ -25645,10 +25691,23 @@ function tokenize(cmd) {
|
|
|
25645
25691
|
while ((m = re.exec(cmd)) !== null) out.push(m[1] ?? m[2] ?? m[3]);
|
|
25646
25692
|
return out;
|
|
25647
25693
|
}
|
|
25694
|
+
function brainExecutable(brain) {
|
|
25695
|
+
const b = (brain || "").trim();
|
|
25696
|
+
if (!b || b === "auto") return null;
|
|
25697
|
+
if (b === "claude") return "claude";
|
|
25698
|
+
if (b === "codex") return "codex";
|
|
25699
|
+
if (b === "openclaw") return "openclaw";
|
|
25700
|
+
if (b === "hermes") return "hermes";
|
|
25701
|
+
if (b === "goose") return "goose";
|
|
25702
|
+
return tokenize(b)[0] || null;
|
|
25703
|
+
}
|
|
25648
25704
|
function brainArgv(brain, prompt) {
|
|
25649
25705
|
const b = resolveBrain(brain);
|
|
25650
25706
|
if (b === "claude") return ["claude", ["-p", "--permission-mode", "acceptEdits", prompt]];
|
|
25651
25707
|
if (b === "codex") return ["codex", ["exec", "--full-auto", prompt]];
|
|
25708
|
+
if (b === "openclaw") return ["openclaw", ["agent", "exec", prompt]];
|
|
25709
|
+
if (b === "hermes") return ["hermes", ["-z", prompt]];
|
|
25710
|
+
if (b === "goose") return ["goose", ["run", "-t", prompt]];
|
|
25652
25711
|
const parts = tokenize(b.trim());
|
|
25653
25712
|
return [parts[0], [...parts.slice(1), prompt]];
|
|
25654
25713
|
}
|
|
@@ -25702,7 +25761,11 @@ function runBrain(cmd, args, cwd) {
|
|
|
25702
25761
|
child.stderr?.on("data", (d) => err += d.toString());
|
|
25703
25762
|
child.on("error", (e) => {
|
|
25704
25763
|
clearTimeout(timer);
|
|
25705
|
-
|
|
25764
|
+
if (e?.code === "ENOENT") {
|
|
25765
|
+
reject(new Error(
|
|
25766
|
+
`the "${cmd}" command isn't installed / on PATH, so I can't run it as your autonomous brain. Install ${cmd}, or pin a different brain: jefri_autonomous(brain:"claude"|"codex"|"openclaw agent exec"|"hermes -z"|"goose run -t"|any command).`
|
|
25767
|
+
));
|
|
25768
|
+
} else reject(e);
|
|
25706
25769
|
});
|
|
25707
25770
|
child.on("close", (code) => {
|
|
25708
25771
|
clearTimeout(timer);
|
|
@@ -25737,7 +25800,20 @@ function handleAutonomous(m, reply, log2) {
|
|
|
25737
25800
|
queue.push(async () => {
|
|
25738
25801
|
const persona = cfg.persona ? cfg.persona.trim() + "\n\n" : "";
|
|
25739
25802
|
const context = await buildContext(m.conversationId);
|
|
25740
|
-
const
|
|
25803
|
+
const fromOwner = !!ownerName && m.sender === ownerName;
|
|
25804
|
+
const facts = [
|
|
25805
|
+
`You are @${selfName}, an autonomous agent on Jefri Chat`,
|
|
25806
|
+
`harness/host app: ${hostName || "unknown"}`,
|
|
25807
|
+
`brain: ${resolveBrain(cfg.brain)}${cfg.brain === "auto" ? " (auto-matched to the host)" : ""}`,
|
|
25808
|
+
`owned by: @${ownerName || "(no owner)"}`,
|
|
25809
|
+
cfg.enabledHost ? `autonomous enabled from ${cfg.enabledHost}${cfg.enabledAt ? ` at ${cfg.enabledAt}` : ""}` : "",
|
|
25810
|
+
fromOwner ? `machine: ${os3.hostname()}` : "",
|
|
25811
|
+
fromOwner ? `working folder: ${cfg.workdir}` : ""
|
|
25812
|
+
].filter(Boolean).join("; ");
|
|
25813
|
+
const runtime = `[Runtime facts \u2014 if asked who set you up or where you're running, answer from THESE, don't guess: ${facts}.]
|
|
25814
|
+
|
|
25815
|
+
`;
|
|
25816
|
+
const prompt = (runtime + (cfg.promptTemplate || DEFAULT_TEMPLATE).replace(/\{persona\}/g, persona).replace(/\{me\}/g, selfName).replace(/\{sender\}/g, m.sender).replace(/\{where\}/g, where).replace(/\{context\}/g, context).replace(/\{message\}/g, m.content)).slice(0, MAX_PROMPT);
|
|
25741
25817
|
const [cmd, args] = brainArgv(cfg.brain, prompt);
|
|
25742
25818
|
const shownBrain = resolveBrain(cfg.brain) + (cfg.brain === "auto" ? " (auto \u2192 host)" : "");
|
|
25743
25819
|
log2(`autonomous: @${m.sender} \u2192 running "${shownBrain}"\u2026`);
|
|
@@ -27016,7 +27092,7 @@ ${cmd}`);
|
|
|
27016
27092
|
description: "Turn autonomous replies on/off for THIS agent. When ON, each incoming message is handled by a 'brain' (claude / codex / a custom command) that does the work in a folder and replies on its own \u2014 so you can, e.g., leave it on and it builds what people ask and answers them. Only works for your own agent (this connector's token). Call with NO arguments to see current settings. \u26A0\uFE0F When on, remote messages drive a tool-enabled agent on your machine \u2014 only keep it on with people you trust, and scope the work folder.",
|
|
27017
27093
|
inputSchema: {
|
|
27018
27094
|
enabled: external_exports.boolean().optional().describe("turn autonomous replies on/off"),
|
|
27019
|
-
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex, Claude Code\u2192claude), or force 'claude' / 'codex' / a full custom command"),
|
|
27095
|
+
brain: external_exports.string().optional().describe("'auto' (default \u2014 matches the host app you launched from: Codex\u2192codex exec, Claude Code\u2192claude -p, OpenClaw\u2192openclaw agent exec, Hermes\u2192hermes -z, Goose\u2192goose run -t, each using that harness's own model), or force 'claude' / 'codex' / 'openclaw' / 'hermes' / 'goose' / a full custom command"),
|
|
27020
27096
|
workdir: external_exports.string().optional().describe("folder the brain works in (scope it!)"),
|
|
27021
27097
|
replyMode: external_exports.enum(["mentions", "dms", "all"]).optional().describe("groups: mentions=only when @-mentioned, all=every message, dms=DMs only"),
|
|
27022
27098
|
persona: external_exports.string().optional().describe("role/instructions for the agent (injected as {persona})"),
|
|
@@ -27082,9 +27158,52 @@ function attachInbox(c, inbox2, self, onIncoming) {
|
|
|
27082
27158
|
|
|
27083
27159
|
// src/doctor.ts
|
|
27084
27160
|
import cp4 from "node:child_process";
|
|
27161
|
+
|
|
27162
|
+
// src/version.ts
|
|
27085
27163
|
import fs5 from "node:fs";
|
|
27086
27164
|
import np4 from "node:path";
|
|
27087
27165
|
import { fileURLToPath } from "node:url";
|
|
27166
|
+
function connectorVersion() {
|
|
27167
|
+
try {
|
|
27168
|
+
const here = np4.dirname(fileURLToPath(import.meta.url));
|
|
27169
|
+
const pkg = JSON.parse(fs5.readFileSync(np4.join(here, "..", "package.json"), "utf8"));
|
|
27170
|
+
return pkg.version ?? "?";
|
|
27171
|
+
} catch {
|
|
27172
|
+
return "?";
|
|
27173
|
+
}
|
|
27174
|
+
}
|
|
27175
|
+
function isNewer(a, b) {
|
|
27176
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
27177
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
27178
|
+
for (let i = 0; i < 3; i++) {
|
|
27179
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
|
|
27180
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
|
|
27181
|
+
}
|
|
27182
|
+
return false;
|
|
27183
|
+
}
|
|
27184
|
+
async function checkForUpdate(timeoutMs = 3e3) {
|
|
27185
|
+
const current = connectorVersion();
|
|
27186
|
+
if (current === "?") return null;
|
|
27187
|
+
const ac = new AbortController();
|
|
27188
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
27189
|
+
try {
|
|
27190
|
+
const r = await fetch("https://registry.npmjs.org/jefrichat-mcp/latest", {
|
|
27191
|
+
signal: ac.signal,
|
|
27192
|
+
headers: { accept: "application/json" }
|
|
27193
|
+
});
|
|
27194
|
+
if (!r.ok) return null;
|
|
27195
|
+
const j = await r.json();
|
|
27196
|
+
const latest = j.version;
|
|
27197
|
+
if (!latest) return null;
|
|
27198
|
+
return { current, latest, outdated: isNewer(latest, current) };
|
|
27199
|
+
} catch {
|
|
27200
|
+
return null;
|
|
27201
|
+
} finally {
|
|
27202
|
+
clearTimeout(timer);
|
|
27203
|
+
}
|
|
27204
|
+
}
|
|
27205
|
+
|
|
27206
|
+
// src/doctor.ts
|
|
27088
27207
|
var T = !!process.stdout.isTTY;
|
|
27089
27208
|
var C = {
|
|
27090
27209
|
g: T ? "\x1B[32m" : "",
|
|
@@ -27112,15 +27231,6 @@ function mask(t) {
|
|
|
27112
27231
|
if (t.length <= 12) return t.slice(0, 4) + "\u2026";
|
|
27113
27232
|
return t.slice(0, 8) + "\u2026" + t.slice(-4);
|
|
27114
27233
|
}
|
|
27115
|
-
function connectorVersion() {
|
|
27116
|
-
try {
|
|
27117
|
-
const here = np4.dirname(fileURLToPath(import.meta.url));
|
|
27118
|
-
const pkg = JSON.parse(fs5.readFileSync(np4.join(here, "..", "package.json"), "utf8"));
|
|
27119
|
-
return pkg.version ?? "?";
|
|
27120
|
-
} catch {
|
|
27121
|
-
return "?";
|
|
27122
|
-
}
|
|
27123
|
-
}
|
|
27124
27234
|
function hasCli(cmd) {
|
|
27125
27235
|
try {
|
|
27126
27236
|
const r = cp4.spawnSync(cmd, ["--version"], {
|
|
@@ -27151,7 +27261,13 @@ ${C.b}\u{1FA7A} Jefri Chat connector \u2014 setup check${C.x}
|
|
|
27151
27261
|
const major = Number(process.versions.node.split(".")[0]);
|
|
27152
27262
|
if (major >= 18) pass(`Node ${process.version}`);
|
|
27153
27263
|
else fail2(`Node ${process.version} is too old`, "Install Node 18+ from https://nodejs.org");
|
|
27154
|
-
|
|
27264
|
+
const upd = await checkForUpdate();
|
|
27265
|
+
if (upd?.outdated)
|
|
27266
|
+
warn(`Connector jefrichat-mcp v${upd.current} \u2014 a newer one exists (v${upd.latest})`, "npx jefrichat-mcp@latest, or 'npm update -g jefrichat-mcp@latest' if globally installed, then reconnect");
|
|
27267
|
+
else if (upd)
|
|
27268
|
+
pass(`Connector jefrichat-mcp v${upd.current} (latest)`);
|
|
27269
|
+
else
|
|
27270
|
+
pass(`Connector jefrichat-mcp v${connectorVersion()}`, "couldn't reach npm to check for updates (offline is fine)");
|
|
27155
27271
|
pass(`Hub URL ${SERVER2}`);
|
|
27156
27272
|
if (!TOKEN2)
|
|
27157
27273
|
fail2("No JEFRI_TOKEN set", "Get your token from jefrichat.com \u2192 Connect, then re-add the connector");
|
|
@@ -27180,9 +27296,17 @@ ${C.b}\u{1FA7A} Jefri Chat connector \u2014 setup check${C.x}
|
|
|
27180
27296
|
info("Skipping hub auth check (no token)");
|
|
27181
27297
|
}
|
|
27182
27298
|
const auto = getAuto();
|
|
27183
|
-
const brainCmd = auto.brain
|
|
27184
|
-
if (
|
|
27185
|
-
|
|
27299
|
+
const brainCmd = brainExecutable(auto.brain);
|
|
27300
|
+
if (brainCmd === null) {
|
|
27301
|
+
const known = ["claude", "codex", "openclaw", "hermes", "goose"];
|
|
27302
|
+
const found = known.filter(hasCli);
|
|
27303
|
+
if (found.length) info(`Autonomous brain "auto" matches your host at runtime \u2014 harnesses on PATH: ${found.join(", ")}`);
|
|
27304
|
+
else info(`Autonomous brain "auto" matches your host at runtime \u2014 none of ${known.join("/")} found on PATH yet (only needed if you enable autonomous mode)`);
|
|
27305
|
+
} else if (hasCli(brainCmd)) {
|
|
27306
|
+
pass(`Autonomous brain "${brainCmd}" found on PATH`);
|
|
27307
|
+
} else {
|
|
27308
|
+
info(`Autonomous brain "${brainCmd}" not on PATH (only needed if you enable autonomous mode)`);
|
|
27309
|
+
}
|
|
27186
27310
|
const prefs2 = getPrefs();
|
|
27187
27311
|
if (!prefs2.enabled) info("Desktop notifications are OFF (use the jefri_notifications tool to turn on)");
|
|
27188
27312
|
else if (macNeedsTerminalNotifier)
|
|
@@ -27242,6 +27366,7 @@ function ensureClient() {
|
|
|
27242
27366
|
if (!clientPromise) {
|
|
27243
27367
|
clientPromise = (async () => {
|
|
27244
27368
|
const known = TOKEN ?? readTokenCache()[cacheKey];
|
|
27369
|
+
const clientInfo = { name: "jefrichat-mcp", version: connectorVersion() };
|
|
27245
27370
|
const provision = () => JefriClient.connect({
|
|
27246
27371
|
server: SERVER,
|
|
27247
27372
|
username: USERNAME,
|
|
@@ -27249,12 +27374,13 @@ function ensureClient() {
|
|
|
27249
27374
|
tags: TAGS,
|
|
27250
27375
|
capabilities: CAPS,
|
|
27251
27376
|
status: "online",
|
|
27252
|
-
ownerToken: process.env.JEFRI_OWNER_TOKEN
|
|
27377
|
+
ownerToken: process.env.JEFRI_OWNER_TOKEN,
|
|
27253
27378
|
// owned by you → shows in your observability
|
|
27379
|
+
clientInfo
|
|
27254
27380
|
});
|
|
27255
27381
|
let c;
|
|
27256
27382
|
if (known) {
|
|
27257
|
-
c = await JefriClient.connect({ server: SERVER, token: known, status: "online" });
|
|
27383
|
+
c = await JefriClient.connect({ server: SERVER, token: known, status: "online", clientInfo });
|
|
27258
27384
|
if (!c.identity) {
|
|
27259
27385
|
if (TOKEN) throw new Error("JEFRI_TOKEN was rejected by the server");
|
|
27260
27386
|
log("cached token rejected, re-provisioning by username");
|
|
@@ -27362,6 +27488,13 @@ async function main() {
|
|
|
27362
27488
|
};
|
|
27363
27489
|
await server.connect(transport);
|
|
27364
27490
|
log(`MCP server ready \u2014 identity @${USERNAME}, Jefri Chat server ${SERVER}`);
|
|
27491
|
+
void checkForUpdate().then((u) => {
|
|
27492
|
+
if (u?.outdated)
|
|
27493
|
+
log(
|
|
27494
|
+
`\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.`
|
|
27495
|
+
);
|
|
27496
|
+
}).catch(() => {
|
|
27497
|
+
});
|
|
27365
27498
|
}
|
|
27366
27499
|
main().catch((e) => {
|
|
27367
27500
|
log("fatal:", e);
|
package/package.json
CHANGED