jefrichat-mcp 0.49.37 → 0.49.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +83 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -63967,7 +63967,9 @@ function nativeHubRequest(client, operation, payload = {}, timeoutMs = 1e4) {
|
|
|
63967
63967
|
return;
|
|
63968
63968
|
}
|
|
63969
63969
|
cleanup();
|
|
63970
|
-
if (!event.result || typeof event.result !== "object" || event.result.ok !== true) reject(Error(
|
|
63970
|
+
if (!event.result || typeof event.result !== "object" || event.result.ok !== true) reject(Error(
|
|
63971
|
+
event.result?.reason === "native_terminal_disabled" ? "Native terminal monitor is disabled on this hub. It needs an operator-approved hub rollout; restarting Claude Code, starting tmux or running jefrichat-mcp run will not enable it. Leave any working headless responder unchanged." : "Native hub refused this binding or operation. The hub may have native monitoring disabled, or this credential/connection is not eligible. Check the selected hub and credential before changing autonomy; do not retry uncertain work."
|
|
63972
|
+
));
|
|
63971
63973
|
else resolve(event.result);
|
|
63972
63974
|
};
|
|
63973
63975
|
const timer2 = setTimeout(() => {
|
|
@@ -64394,6 +64396,23 @@ function codexQueueArgs(thread, text5, endpoint) {
|
|
|
64394
64396
|
throw Error("Native Codex preview accepts only private local Unix endpoints");
|
|
64395
64397
|
return ["queue", "--thread", thread, "--message", text5, ...endpoint ? ["--remote", endpoint] : []];
|
|
64396
64398
|
}
|
|
64399
|
+
var codexProcessEnv = () => Object.fromEntries(
|
|
64400
|
+
["PATH", "HOME", "USER", "LOGNAME", "LANG", "LC_ALL", "TMPDIR", "SHELL", "CODEX_HOME", "XDG_CONFIG_HOME"].flatMap((k) => process.env[k] === void 0 ? [] : [[k, process.env[k]]])
|
|
64401
|
+
);
|
|
64402
|
+
async function checkCodexQueue() {
|
|
64403
|
+
await new Promise((resolve, reject) => {
|
|
64404
|
+
execFile(
|
|
64405
|
+
"codex",
|
|
64406
|
+
["queue", "--help"],
|
|
64407
|
+
{ env: codexProcessEnv(), timeout: 5e3, maxBuffer: 128e3, windowsHide: true },
|
|
64408
|
+
(error2, stdout) => {
|
|
64409
|
+
if (error2 || !stdout.includes("--thread") || !stdout.includes("--message"))
|
|
64410
|
+
reject(Error("This Codex CLI does not expose the native queue interface. Update Codex before enabling monitor; leave any working responder unchanged."));
|
|
64411
|
+
else resolve();
|
|
64412
|
+
}
|
|
64413
|
+
);
|
|
64414
|
+
});
|
|
64415
|
+
}
|
|
64397
64416
|
function validateCodexEndpoint(endpoint) {
|
|
64398
64417
|
if (!endpoint || endpoint === "unix://") return;
|
|
64399
64418
|
if (!endpoint.startsWith("unix:///")) throw Error("Native Codex endpoint must be a private local socket");
|
|
@@ -64415,9 +64434,7 @@ function codexQueueTransport(thread, endpoint) {
|
|
|
64415
64434
|
validateCodexEndpoint(endpoint);
|
|
64416
64435
|
let open = true;
|
|
64417
64436
|
const children = /* @__PURE__ */ new Set();
|
|
64418
|
-
const env =
|
|
64419
|
-
for (const k of ["PATH", "HOME", "USER", "LOGNAME", "LANG", "LC_ALL", "TMPDIR", "SHELL", "CODEX_HOME", "XDG_CONFIG_HOME"])
|
|
64420
|
-
if (process.env[k] !== void 0) env[k] = process.env[k];
|
|
64437
|
+
const env = codexProcessEnv();
|
|
64421
64438
|
return {
|
|
64422
64439
|
host: "codex_local",
|
|
64423
64440
|
target: thread,
|
|
@@ -64474,7 +64491,7 @@ function registerTerminalLive(server2, deps) {
|
|
|
64474
64491
|
};
|
|
64475
64492
|
const result = (data) => ({ content: [{ type: "text", text: JSON.stringify(data) }] });
|
|
64476
64493
|
const wrap2 = (fn) => async (args, extra) => {
|
|
64477
|
-
const signal = args.action === "off" || args.action === "status" ? void 0 : extra?.signal;
|
|
64494
|
+
const signal = args.action === "off" || args.action === "status" || args.action === "check" ? void 0 : extra?.signal;
|
|
64478
64495
|
const abort = () => stop("native tool cancelled by host");
|
|
64479
64496
|
try {
|
|
64480
64497
|
if (signal?.aborted) throw Error("Native request already cancelled");
|
|
@@ -64483,17 +64500,32 @@ function registerTerminalLive(server2, deps) {
|
|
|
64483
64500
|
if (signal?.aborted) throw Error("Native request cancelled; already-dispatched effects cannot be undone");
|
|
64484
64501
|
return result(data);
|
|
64485
64502
|
} catch (e) {
|
|
64486
|
-
return { ...result({
|
|
64503
|
+
return { ...result({
|
|
64504
|
+
error: e instanceof Error ? e.message : "Native live refused",
|
|
64505
|
+
...args.action ? { hub: deps.hub(), host: host ?? null, username: deps.currentClient()?.identity?.username ?? null } : {}
|
|
64506
|
+
}), isError: true };
|
|
64487
64507
|
} finally {
|
|
64488
64508
|
signal?.removeEventListener("abort", abort);
|
|
64489
64509
|
}
|
|
64490
64510
|
};
|
|
64491
64511
|
const host = deps.env.JEFRI_TERMINAL_HOST;
|
|
64492
64512
|
const supported = (h) => NATIVE_PILOT_PROFILES.includes(String(h));
|
|
64513
|
+
const verifyAuthority = async (c2, stillCurrent, operation = "check", payload = {}) => {
|
|
64514
|
+
if (!supported(host)) throw Error(`Native preview hosts are ${NATIVE_PILOT_PROFILES.join(" or ")}; set JEFRI_TERMINAL_HOST to the matching local connector host.`);
|
|
64515
|
+
if (!c2.identity?.username || !c2.identity.owner || !c2.token) throw Error("A named credential and its owner are required");
|
|
64516
|
+
const answer = await credentialProfileDecision(deps.hub(), c2.token);
|
|
64517
|
+
const profile = answer.lookup.kind === "profile" ? answer.lookup.profile : null;
|
|
64518
|
+
if (!stillCurrent() || profile !== host || !mechanismAllowed(profile, true, "terminal_live", true) || answer.verifiedAt === null || !credentialProfileDecisionCurrent(answer))
|
|
64519
|
+
throw Error("Native live refused: the current connection needs a verified credential for this terminal host. Check its identity, hub and connection; changing autonomy will not repair a credential mismatch.");
|
|
64520
|
+
const permission = await nativeHubRequest(c2, operation, payload);
|
|
64521
|
+
if (!stillCurrent() || !credentialProfileDecisionCurrent(answer) || operation === "check" && permission.protocol !== 1)
|
|
64522
|
+
throw Error("Native hub fencing is unavailable for this binding");
|
|
64523
|
+
return { ...answer, verifiedAt: answer.verifiedAt };
|
|
64524
|
+
};
|
|
64493
64525
|
server2.registerTool("jefri_terminal_live", {
|
|
64494
64526
|
title: "Native terminal live (preview)",
|
|
64495
|
-
description: "
|
|
64496
|
-
inputSchema: { action: external_exports.enum(["status", "prepare", "off", "recover"]), confirmPreview: external_exports.boolean().optional(), replyMode: external_exports.enum(["mentions", "all", "dms"]).optional(), cursor: external_exports.string().max(256).optional(), localCursor: external_exports.string().max(256).optional(), threadId: external_exports.string().uuid().optional() }
|
|
64527
|
+
description: "Native terminal monitor ON/OFF: experimental visible automatic replies in THIS conversation, without tmux. 'monitor on' explicitly requests this preview, not headless or tmux. Use THIS connection's selected agent; verify with jefri_whoami and do not ask the user to choose again when already bound. FIRST call check on the selected identity BEFORE asking to turn off headless/tmux/standby; check tests hub and credential eligibility without changing any responder or arming a watch. If refused, leave working autonomy unchanged. After a successful check, explicitly confirm any switch with the user; headless/tmux/standby must be OFF for prepare. prepare requires confirmPreview:true and sends a wake probe; only its callback can arm this exact MCP session. In Codex, run `printenv CODEX_THREAD_ID` in THIS conversation and pass it as threadId; do not ask the user for it or guess from history. Uses owner-side sender, sibling-agent loop brake and group routing; respects group pause/membership. off changes no saved autonomy config. recover never replays work: nextLocalCursor feeds localCursor, hubPending.nextCursor feeds cursor independently; null ends a scan. Requires the current interactive Claude Code/Codex session to stay open, NOT jefrichat-mcp run or tmux. A successful check is not proof of Monitor attachment or delivery.",
|
|
64528
|
+
inputSchema: { action: external_exports.enum(["status", "check", "prepare", "off", "recover"]), confirmPreview: external_exports.boolean().optional(), replyMode: external_exports.enum(["mentions", "all", "dms"]).optional(), cursor: external_exports.string().max(256).optional(), localCursor: external_exports.string().max(256).optional(), threadId: external_exports.string().uuid().optional() }
|
|
64497
64529
|
}, wrap2(async (args) => {
|
|
64498
64530
|
if (args.action === "off") {
|
|
64499
64531
|
stop();
|
|
@@ -64505,7 +64537,40 @@ function registerTerminalLive(server2, deps) {
|
|
|
64505
64537
|
rollback: "Saved headless/tmux configuration is unchanged. Re-enable it explicitly if wanted."
|
|
64506
64538
|
};
|
|
64507
64539
|
}
|
|
64508
|
-
if (args.action === "status") return {
|
|
64540
|
+
if (args.action === "status") return {
|
|
64541
|
+
...broker.status(),
|
|
64542
|
+
preparing: !!pending,
|
|
64543
|
+
hub: deps.hub(),
|
|
64544
|
+
username: deps.currentClient()?.identity?.username ?? null,
|
|
64545
|
+
autonomyEnabled: getAuto().enabled,
|
|
64546
|
+
scope: "owner-side DMs and groups; experimental; current interactive session must stay open; no 10k certification"
|
|
64547
|
+
};
|
|
64548
|
+
if (args.action === "check") {
|
|
64549
|
+
const c3 = deps.currentClient();
|
|
64550
|
+
if (!c3?.online) throw Error("Select and connect a Jefri identity in THIS session first: use jefri_whoami to verify username and hub, and jefri_be if unassigned. Do not take over another session without the user's approval.");
|
|
64551
|
+
const hub2 = deps.hub(), token2 = c3.token, connectionGeneration2 = c3.generation;
|
|
64552
|
+
const self2 = c3.identity?.username, owner3 = c3.identity?.owner, attempt2 = generation;
|
|
64553
|
+
const stillCurrent = () => attempt2 === generation && deps.currentClient() === c3 && deps.hub() === hub2 && c3.online && c3.token === token2 && c3.generation === connectionGeneration2 && c3.identity?.username === self2 && c3.identity?.owner === owner3;
|
|
64554
|
+
const authority = await verifyAuthority(c3, stillCurrent);
|
|
64555
|
+
if (host === "codex_local") await checkCodexQueue();
|
|
64556
|
+
if (!stillCurrent() || !credentialProfileDecisionCurrent(authority)) throw Error("Native binding superseded during preflight");
|
|
64557
|
+
const auto = getAuto(), standby = standbyEngaged(), nativeActive = broker.active() || !!pending;
|
|
64558
|
+
return {
|
|
64559
|
+
state: "preflight only; no settings changed",
|
|
64560
|
+
hub: hub2,
|
|
64561
|
+
username: self2,
|
|
64562
|
+
host,
|
|
64563
|
+
hubEligible: true,
|
|
64564
|
+
canPrepare: !auto.enabled && !standby && !nativeActive,
|
|
64565
|
+
autonomyEnabled: auto.enabled,
|
|
64566
|
+
autonomyMode: auto.mode,
|
|
64567
|
+
standbyEngaged: standby,
|
|
64568
|
+
nativeActive,
|
|
64569
|
+
...host === "codex_local" ? { threadInstruction: "Run `printenv CODEX_THREAD_ID` in THIS Codex conversation, then pass that exact UUID as threadId to prepare. Do not ask the user for a thread ID; do not use a name, --last or history lookup." } : {},
|
|
64570
|
+
nextStep: nativeActive ? "A native binding is already active or preparing; inspect status, do not start a second watch." : auto.enabled || standby ? "Hub check passed. Ask the user before switching off existing headless/tmux/standby; disabling can cancel in-flight work. Then prepare with explicit preview consent." : "With explicit user preview consent, call prepare. Only the wake callback can confirm this conversation is armed.",
|
|
64571
|
+
warning: "Eligibility is checked again at prepare. Monitor attachment and real host delivery have not been tested by this check."
|
|
64572
|
+
};
|
|
64573
|
+
}
|
|
64509
64574
|
if (args.action === "recover") return broker.recover(args.cursor, args.localCursor);
|
|
64510
64575
|
if (args.confirmPreview !== true) throw Error("Explicit user opt-in required: confirmPreview:true");
|
|
64511
64576
|
if (host === "codex_local" && !deps.env.JEFRI_TERMINAL_THREAD_ID && !args.threadId)
|
|
@@ -64543,14 +64608,13 @@ function registerTerminalLive(server2, deps) {
|
|
|
64543
64608
|
};
|
|
64544
64609
|
const payload = (r) => ({ sourceId: r.id, bindingId, wake: r.wake, replyId: r.reply });
|
|
64545
64610
|
const replyMode = args.replyMode ?? getAuto().replyMode;
|
|
64546
|
-
const
|
|
64611
|
+
const connected = () => attempt === generation && deps.currentClient() === c2 && deps.hub() === hub && c2.token === token && c2.generation === connectionGeneration && c2.identity?.username === self && c2.identity?.owner === owner2 && c2.online;
|
|
64612
|
+
const current = () => connected() && !getAuto().enabled && !standbyEngaged();
|
|
64613
|
+
if (host === "codex_local") await checkCodexQueue();
|
|
64547
64614
|
const authorize = async () => {
|
|
64548
|
-
const answer = await
|
|
64549
|
-
|
|
64550
|
-
if (!current()
|
|
64551
|
-
throw Error("Native live refused: credential profile, binding or authority unavailable; existing autonomy must be off");
|
|
64552
|
-
const hubPermission = await nativeHubRequest(c2, activated ? "renew" : "check", { bindingId });
|
|
64553
|
-
if (!current() || !activated && hubPermission.protocol !== 1) throw Error("Native hub fencing is unavailable for this binding");
|
|
64615
|
+
const answer = await verifyAuthority(c2, activated ? current : connected, activated ? "renew" : "check", { bindingId });
|
|
64616
|
+
if (!connected()) throw Error("Native binding superseded");
|
|
64617
|
+
if (!current()) throw Error("Native live cannot prepare: existing headless/tmux/standby must be off. The hub check passed, but ask the user before switching: disabling can cancel in-flight work. Nothing was changed.");
|
|
64554
64618
|
return { verifiedAt: answer.verifiedAt, current: () => current() && credentialProfileDecisionCurrent(answer) };
|
|
64555
64619
|
};
|
|
64556
64620
|
await authorize();
|
|
@@ -65592,7 +65656,6 @@ async function runDoctor() {
|
|
|
65592
65656
|
pass(`Connector jefrichat-mcp v${upd.current} (latest)`);
|
|
65593
65657
|
else
|
|
65594
65658
|
pass(`Connector jefrichat-mcp v${connectorVersion()}`, "couldn't reach npm to check for updates (offline is fine)");
|
|
65595
|
-
pass(`Hub URL ${SERVER2}`);
|
|
65596
65659
|
let savedEntries = [];
|
|
65597
65660
|
let savedError = null;
|
|
65598
65661
|
if (!TOKEN2) {
|
|
@@ -65602,6 +65665,9 @@ async function runDoctor() {
|
|
|
65602
65665
|
savedError = String(e?.message ?? e);
|
|
65603
65666
|
}
|
|
65604
65667
|
}
|
|
65668
|
+
if (TOKEN2) info(`Hub URL ${SERVER2}`);
|
|
65669
|
+
else if (savedEntries.length) info("Saved-identity mode \u2014 each identity uses its saved hub (listed below); no identity is selected by doctor.");
|
|
65670
|
+
else info(`Default hub URL ${SERVER2} (no identity selected)`);
|
|
65605
65671
|
const lock2 = describeLock(IDENTITIES_FILE());
|
|
65606
65672
|
if (lock2.present)
|
|
65607
65673
|
warn(`identities.json is locked (${lock2.meta ? `pid ${lock2.meta.pid}, ${lock2.status}` : "unreadable \u2014 ownership unknown"})`, lock2.status === "dead" ? `run: ${unlockCommandHint()}` : lock2.meta ? "a save may be in progress; if that process is gone, run: " + unlockCommandHint() : "see the identity unlock message for the safe recovery step");
|