jefrichat-mcp 0.49.18 → 0.49.21
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/build-inputs.json +1 -1
- package/dist/http.js +198 -16
- package/dist/index.js +570 -253
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -480,8 +480,8 @@ var init_parseUtil = __esm({
|
|
|
480
480
|
init_errors();
|
|
481
481
|
init_en();
|
|
482
482
|
makeIssue = (params) => {
|
|
483
|
-
const { data, path:
|
|
484
|
-
const fullPath = [...
|
|
483
|
+
const { data, path: path17, errorMaps, issueData } = params;
|
|
484
|
+
const fullPath = [...path17, ...issueData.path || []];
|
|
485
485
|
const fullIssue = {
|
|
486
486
|
...issueData,
|
|
487
487
|
path: fullPath
|
|
@@ -789,11 +789,11 @@ var init_types = __esm({
|
|
|
789
789
|
init_parseUtil();
|
|
790
790
|
init_util();
|
|
791
791
|
ParseInputLazyPath = class {
|
|
792
|
-
constructor(parent, value,
|
|
792
|
+
constructor(parent, value, path17, key) {
|
|
793
793
|
this._cachedPath = [];
|
|
794
794
|
this.parent = parent;
|
|
795
795
|
this.data = value;
|
|
796
|
-
this._path =
|
|
796
|
+
this._path = path17;
|
|
797
797
|
this._key = key;
|
|
798
798
|
}
|
|
799
799
|
get path() {
|
|
@@ -4373,10 +4373,10 @@ function assignProp(target, prop, value) {
|
|
|
4373
4373
|
configurable: true
|
|
4374
4374
|
});
|
|
4375
4375
|
}
|
|
4376
|
-
function getElementAtPath(obj,
|
|
4377
|
-
if (!
|
|
4376
|
+
function getElementAtPath(obj, path17) {
|
|
4377
|
+
if (!path17)
|
|
4378
4378
|
return obj;
|
|
4379
|
-
return
|
|
4379
|
+
return path17.reduce((acc, key) => acc?.[key], obj);
|
|
4380
4380
|
}
|
|
4381
4381
|
function promiseAllObject(promisesObj) {
|
|
4382
4382
|
const keys = Object.keys(promisesObj);
|
|
@@ -4625,11 +4625,11 @@ function aborted(x, startIndex = 0) {
|
|
|
4625
4625
|
}
|
|
4626
4626
|
return false;
|
|
4627
4627
|
}
|
|
4628
|
-
function prefixIssues(
|
|
4628
|
+
function prefixIssues(path17, issues) {
|
|
4629
4629
|
return issues.map((iss) => {
|
|
4630
4630
|
var _a;
|
|
4631
4631
|
(_a = iss).path ?? (_a.path = []);
|
|
4632
|
-
iss.path.unshift(
|
|
4632
|
+
iss.path.unshift(path17);
|
|
4633
4633
|
return iss;
|
|
4634
4634
|
});
|
|
4635
4635
|
}
|
|
@@ -16457,8 +16457,8 @@ var require_utils = __commonJS({
|
|
|
16457
16457
|
}
|
|
16458
16458
|
return ind;
|
|
16459
16459
|
}
|
|
16460
|
-
function removeDotSegments(
|
|
16461
|
-
let input2 =
|
|
16460
|
+
function removeDotSegments(path17) {
|
|
16461
|
+
let input2 = path17;
|
|
16462
16462
|
const output2 = [];
|
|
16463
16463
|
let nextSlash = -1;
|
|
16464
16464
|
let len = 0;
|
|
@@ -16710,8 +16710,8 @@ var require_schemes = __commonJS({
|
|
|
16710
16710
|
wsComponent.secure = void 0;
|
|
16711
16711
|
}
|
|
16712
16712
|
if (wsComponent.resourceName) {
|
|
16713
|
-
const [
|
|
16714
|
-
wsComponent.path =
|
|
16713
|
+
const [path17, query] = wsComponent.resourceName.split("?");
|
|
16714
|
+
wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
|
|
16715
16715
|
wsComponent.query = query;
|
|
16716
16716
|
wsComponent.resourceName = void 0;
|
|
16717
16717
|
}
|
|
@@ -20104,12 +20104,12 @@ var require_dist = __commonJS({
|
|
|
20104
20104
|
throw new Error(`Unknown format "${name}"`);
|
|
20105
20105
|
return f;
|
|
20106
20106
|
};
|
|
20107
|
-
function addFormats(ajv, list3,
|
|
20107
|
+
function addFormats(ajv, list3, fs35, exportName) {
|
|
20108
20108
|
var _a;
|
|
20109
20109
|
var _b;
|
|
20110
20110
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
20111
20111
|
for (const f of list3)
|
|
20112
|
-
ajv.addFormat(f,
|
|
20112
|
+
ajv.addFormat(f, fs35[f]);
|
|
20113
20113
|
}
|
|
20114
20114
|
module.exports = exports = formatsPlugin;
|
|
20115
20115
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -22288,8 +22288,8 @@ function assertNonEmpty(part, name) {
|
|
|
22288
22288
|
throw new Error("`" + name + "` cannot be empty");
|
|
22289
22289
|
}
|
|
22290
22290
|
}
|
|
22291
|
-
function assertPath(
|
|
22292
|
-
if (!
|
|
22291
|
+
function assertPath(path17, name) {
|
|
22292
|
+
if (!path17) {
|
|
22293
22293
|
throw new Error("Setting `" + name + "` requires `path` to be set too");
|
|
22294
22294
|
}
|
|
22295
22295
|
}
|
|
@@ -22474,13 +22474,13 @@ var init_lib4 = __esm({
|
|
|
22474
22474
|
* @returns {undefined}
|
|
22475
22475
|
* Nothing.
|
|
22476
22476
|
*/
|
|
22477
|
-
set path(
|
|
22478
|
-
if (isUrl(
|
|
22479
|
-
|
|
22477
|
+
set path(path17) {
|
|
22478
|
+
if (isUrl(path17)) {
|
|
22479
|
+
path17 = fileURLToPath(path17);
|
|
22480
22480
|
}
|
|
22481
|
-
assertNonEmpty(
|
|
22482
|
-
if (this.path !==
|
|
22483
|
-
this.history.push(
|
|
22481
|
+
assertNonEmpty(path17, "path");
|
|
22482
|
+
if (this.path !== path17) {
|
|
22483
|
+
this.history.push(path17);
|
|
22484
22484
|
}
|
|
22485
22485
|
}
|
|
22486
22486
|
/**
|
|
@@ -31190,7 +31190,7 @@ function transformGfmAutolinkLiterals(tree) {
|
|
|
31190
31190
|
{ ignore: ["link", "linkReference"] }
|
|
31191
31191
|
);
|
|
31192
31192
|
}
|
|
31193
|
-
function findUrl(_, protocol, domain2,
|
|
31193
|
+
function findUrl(_, protocol, domain2, path17, match) {
|
|
31194
31194
|
let prefix = "";
|
|
31195
31195
|
if (!previous2(match)) {
|
|
31196
31196
|
return false;
|
|
@@ -31203,7 +31203,7 @@ function findUrl(_, protocol, domain2, path15, match) {
|
|
|
31203
31203
|
if (!isCorrectDomain(domain2)) {
|
|
31204
31204
|
return false;
|
|
31205
31205
|
}
|
|
31206
|
-
const parts = splitUrl(domain2 +
|
|
31206
|
+
const parts = splitUrl(domain2 + path17);
|
|
31207
31207
|
if (!parts[0]) return false;
|
|
31208
31208
|
const result = {
|
|
31209
31209
|
type: "link",
|
|
@@ -38608,6 +38608,19 @@ function connectErrorFromEvent(ev) {
|
|
|
38608
38608
|
if (ev?.minVersion) err.minVersion = ev.minVersion;
|
|
38609
38609
|
return err;
|
|
38610
38610
|
}
|
|
38611
|
+
function terminalConnectionCode(ev) {
|
|
38612
|
+
if (ev.code) return [
|
|
38613
|
+
"auth",
|
|
38614
|
+
"auth_failed",
|
|
38615
|
+
"authentication_failed",
|
|
38616
|
+
"invalid_token",
|
|
38617
|
+
"ACP_AUTH_FAILED",
|
|
38618
|
+
"upgrade_required",
|
|
38619
|
+
"taken_over",
|
|
38620
|
+
"superseded"
|
|
38621
|
+
].includes(ev.code) ? ev.code : null;
|
|
38622
|
+
return /^(?:authentication failed|invalid or expired (?:Jefri Chat )?token)(?:\b|$)/i.test(ev.message ?? "") ? "auth" : null;
|
|
38623
|
+
}
|
|
38611
38624
|
var subtle, enc, dec, E2E_ALG, e2eDir, b64, fromB64, safeName, canonicalJwk, JefriAuthError, JefriClient, serveDir;
|
|
38612
38625
|
var init_src2 = __esm({
|
|
38613
38626
|
"../sdk/src/index.ts"() {
|
|
@@ -38634,6 +38647,17 @@ var init_src2 = __esm({
|
|
|
38634
38647
|
};
|
|
38635
38648
|
JefriClient = class _JefriClient {
|
|
38636
38649
|
ws;
|
|
38650
|
+
recoveryEnabled = false;
|
|
38651
|
+
registeredSocket;
|
|
38652
|
+
retireRecoverySocket;
|
|
38653
|
+
recoveryRegisterTimer;
|
|
38654
|
+
get registeredRecoveryEnabled() {
|
|
38655
|
+
return this.recoveryEnabled;
|
|
38656
|
+
}
|
|
38657
|
+
get connectionStatus() {
|
|
38658
|
+
if (this.terminalCode || this.closing) return "stopped";
|
|
38659
|
+
return this.online ? "online" : this.hasConnected ? "reconnecting" : "connecting";
|
|
38660
|
+
}
|
|
38637
38661
|
handlers = /* @__PURE__ */ new Map();
|
|
38638
38662
|
/** Why the client stopped reconnecting: "auth" | "upgrade_required" | "taken_over". */
|
|
38639
38663
|
terminalCode = null;
|
|
@@ -38661,6 +38685,14 @@ var init_src2 = __esm({
|
|
|
38661
38685
|
* so pre-0.49 SDKs stop via their /auth/i match. */
|
|
38662
38686
|
noteTerminalError(ev) {
|
|
38663
38687
|
if (ev?.event !== "error") return;
|
|
38688
|
+
if (this.recoveryEnabled) {
|
|
38689
|
+
const code3 = terminalConnectionCode(ev);
|
|
38690
|
+
if (code3) {
|
|
38691
|
+
this.authFailed = true;
|
|
38692
|
+
this.terminalCode = code3;
|
|
38693
|
+
}
|
|
38694
|
+
return;
|
|
38695
|
+
}
|
|
38664
38696
|
if (/auth/i.test(ev.message ?? "") || ev.code === "upgrade_required" || ev.code === "taken_over" || ev.code === "superseded") {
|
|
38665
38697
|
this.authFailed = true;
|
|
38666
38698
|
this.terminalCode = ev.code ?? "auth";
|
|
@@ -38756,6 +38788,8 @@ var init_src2 = __esm({
|
|
|
38756
38788
|
* "offline" honestly instead of claiming an agent can be reached and then
|
|
38757
38789
|
* failing on send. */
|
|
38758
38790
|
get online() {
|
|
38791
|
+
if (this.recoveryEnabled)
|
|
38792
|
+
return !this.closing && !this.authFailed && !!this.ws && this.ws.readyState === wrapper_default.OPEN && this.registeredSocket === this.ws;
|
|
38759
38793
|
return this.hasConnected && !this.closing && this.ws?.readyState === this.ws?.OPEN;
|
|
38760
38794
|
}
|
|
38761
38795
|
constructor(server2) {
|
|
@@ -38764,6 +38798,7 @@ var init_src2 = __esm({
|
|
|
38764
38798
|
static async connect(opts = {}) {
|
|
38765
38799
|
const server2 = (opts.server ?? "https://jefrichat.com").replace(/\/$/, "");
|
|
38766
38800
|
const client = new _JefriClient(server2);
|
|
38801
|
+
client.recoveryEnabled = opts.registeredRecovery === true;
|
|
38767
38802
|
let token = opts.token;
|
|
38768
38803
|
if (!token) {
|
|
38769
38804
|
const res = await fetch(`${server2}/api/agents`, {
|
|
@@ -38803,7 +38838,9 @@ var init_src2 = __esm({
|
|
|
38803
38838
|
fn();
|
|
38804
38839
|
};
|
|
38805
38840
|
onReg = () => finish(resolve);
|
|
38806
|
-
onErr = (ev) => finish(() => reject(
|
|
38841
|
+
onErr = (ev) => finish(() => reject(
|
|
38842
|
+
client.recoveryEnabled && !ev?.code && !terminalConnectionCode(ev) ? new Error(ev?.message ?? "hub rejected the connection") : connectErrorFromEvent(ev)
|
|
38843
|
+
));
|
|
38807
38844
|
const regTimeoutMs = typeof process !== "undefined" && Number(process.env?.JEFRI_REGISTER_TIMEOUT_MS) || 8e3;
|
|
38808
38845
|
timer = setTimeout(
|
|
38809
38846
|
() => finish(() => reject(new Error("timed out waiting for the hub to register the connection"))),
|
|
@@ -38830,11 +38867,47 @@ var init_src2 = __esm({
|
|
|
38830
38867
|
return client;
|
|
38831
38868
|
}
|
|
38832
38869
|
openSocket() {
|
|
38870
|
+
if (this.recoveryEnabled) {
|
|
38871
|
+
this.retireRecoverySocket?.();
|
|
38872
|
+
this.registeredSocket = void 0;
|
|
38873
|
+
}
|
|
38833
38874
|
const wsUrl = this.server.replace(/^http/, "ws") + `/ws`;
|
|
38834
38875
|
this.ws = new wrapper_default(wsUrl, { headers: this.socketHeaders() });
|
|
38876
|
+
const ws = this.ws;
|
|
38877
|
+
const current = () => !this.recoveryEnabled || this.ws === ws && !this.closing && !this.authFailed;
|
|
38835
38878
|
return new Promise((resolve, reject) => {
|
|
38836
38879
|
let settled = false;
|
|
38880
|
+
let registrationTimer;
|
|
38881
|
+
const clearRegistrationTimer = () => {
|
|
38882
|
+
clearTimeout(registrationTimer);
|
|
38883
|
+
if (this.recoveryRegisterTimer === registrationTimer) this.recoveryRegisterTimer = void 0;
|
|
38884
|
+
registrationTimer = void 0;
|
|
38885
|
+
};
|
|
38886
|
+
if (this.recoveryEnabled) {
|
|
38887
|
+
const timeoutMs = Number(process.env.JEFRI_REGISTER_TIMEOUT_MS) || 8e3;
|
|
38888
|
+
registrationTimer = setTimeout(() => {
|
|
38889
|
+
if (!current() || this.registeredSocket === ws) return;
|
|
38890
|
+
if (!settled) {
|
|
38891
|
+
settled = true;
|
|
38892
|
+
reject(new Error("timed out waiting for the hub to register the connection"));
|
|
38893
|
+
}
|
|
38894
|
+
ws.terminate();
|
|
38895
|
+
}, timeoutMs);
|
|
38896
|
+
this.recoveryRegisterTimer = registrationTimer;
|
|
38897
|
+
this.retireRecoverySocket = () => {
|
|
38898
|
+
clearRegistrationTimer();
|
|
38899
|
+
if (!settled) {
|
|
38900
|
+
settled = true;
|
|
38901
|
+
reject(new Error("connection attempt cancelled"));
|
|
38902
|
+
}
|
|
38903
|
+
ws.removeAllListeners();
|
|
38904
|
+
ws.on("error", () => {
|
|
38905
|
+
});
|
|
38906
|
+
ws.terminate();
|
|
38907
|
+
};
|
|
38908
|
+
}
|
|
38837
38909
|
this.ws.on("open", () => {
|
|
38910
|
+
if (!current()) return;
|
|
38838
38911
|
this.startPing();
|
|
38839
38912
|
this.emit("socket_open", {});
|
|
38840
38913
|
try {
|
|
@@ -38850,17 +38923,28 @@ var init_src2 = __esm({
|
|
|
38850
38923
|
}
|
|
38851
38924
|
});
|
|
38852
38925
|
this.ws.on("error", (e) => {
|
|
38926
|
+
if (this.recoveryEnabled && this.ws !== ws) return;
|
|
38853
38927
|
if (!settled) {
|
|
38854
38928
|
settled = true;
|
|
38855
38929
|
reject(e);
|
|
38856
38930
|
}
|
|
38857
38931
|
});
|
|
38858
38932
|
this.ws.on("close", () => {
|
|
38933
|
+
if (this.recoveryEnabled) {
|
|
38934
|
+
clearRegistrationTimer();
|
|
38935
|
+
if (this.ws !== ws) return;
|
|
38936
|
+
this.registeredSocket = void 0;
|
|
38937
|
+
if (!settled) {
|
|
38938
|
+
settled = true;
|
|
38939
|
+
reject(new Error("connection closed before opening"));
|
|
38940
|
+
}
|
|
38941
|
+
}
|
|
38859
38942
|
this.stopPing();
|
|
38860
38943
|
this.emit("socket_close", {});
|
|
38861
38944
|
if (this.hasConnected && !this.closing && !this.authFailed) this.scheduleReconnect();
|
|
38862
38945
|
});
|
|
38863
38946
|
this.ws.on("message", (data) => {
|
|
38947
|
+
if (!current()) return;
|
|
38864
38948
|
let ev;
|
|
38865
38949
|
try {
|
|
38866
38950
|
ev = JSON.parse(data.toString());
|
|
@@ -38868,6 +38952,10 @@ var init_src2 = __esm({
|
|
|
38868
38952
|
return;
|
|
38869
38953
|
}
|
|
38870
38954
|
if (ev.event === "registered") {
|
|
38955
|
+
if (this.recoveryEnabled) {
|
|
38956
|
+
this.registeredSocket = ws;
|
|
38957
|
+
clearRegistrationTimer();
|
|
38958
|
+
}
|
|
38871
38959
|
this.identity = ev.identity;
|
|
38872
38960
|
this.generation = typeof ev.generation === "string" ? ev.generation : null;
|
|
38873
38961
|
const rev = ev.identity?.statusRev;
|
|
@@ -38876,6 +38964,14 @@ var init_src2 = __esm({
|
|
|
38876
38964
|
this.reconnectAttempts = 0;
|
|
38877
38965
|
}
|
|
38878
38966
|
this.noteTerminalError(ev);
|
|
38967
|
+
if (this.recoveryEnabled && this.authFailed) {
|
|
38968
|
+
clearRegistrationTimer();
|
|
38969
|
+
this.registeredSocket = void 0;
|
|
38970
|
+
this.stopPing();
|
|
38971
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
38972
|
+
this.reconnectTimer = void 0;
|
|
38973
|
+
ws.close();
|
|
38974
|
+
}
|
|
38879
38975
|
this.notePresenceAck(ev);
|
|
38880
38976
|
this.emit(ev.event, ev);
|
|
38881
38977
|
this.emit("*", ev);
|
|
@@ -38891,14 +38987,17 @@ var init_src2 = __esm({
|
|
|
38891
38987
|
// triggers the existing reconnect.
|
|
38892
38988
|
startPing() {
|
|
38893
38989
|
this.stopPing();
|
|
38990
|
+
const ws = this.ws;
|
|
38991
|
+
const current = () => !this.recoveryEnabled || this.ws === ws && !this.closing && !this.authFailed;
|
|
38894
38992
|
this.isAlive = true;
|
|
38895
38993
|
try {
|
|
38896
38994
|
this.ws.on("pong", () => {
|
|
38897
|
-
this.isAlive = true;
|
|
38995
|
+
if (current()) this.isAlive = true;
|
|
38898
38996
|
});
|
|
38899
38997
|
} catch {
|
|
38900
38998
|
}
|
|
38901
38999
|
this.pingTimer = setInterval(() => {
|
|
39000
|
+
if (!current()) return;
|
|
38902
39001
|
if (this.ws.readyState !== this.ws.OPEN) return;
|
|
38903
39002
|
if (!this.isAlive) {
|
|
38904
39003
|
try {
|
|
@@ -38988,6 +39087,12 @@ var init_src2 = __esm({
|
|
|
38988
39087
|
this.dispatch(event, payload, this.handlers.get(event) ?? []);
|
|
38989
39088
|
}
|
|
38990
39089
|
send(ev) {
|
|
39090
|
+
if (this.recoveryEnabled && !this.online) {
|
|
39091
|
+
if (this.terminalCode)
|
|
39092
|
+
throw new Error(`Jefri Chat connection stopped (${this.terminalCode}) \u2014 not reconnecting automatically; check this desktop session's connection`);
|
|
39093
|
+
if (this.closing) throw new Error("Jefri Chat connection closed \u2014 restart this desktop connector");
|
|
39094
|
+
throw new Error("Jefri Chat hub connection is not registered yet (reconnecting) \u2014 try again after registration");
|
|
39095
|
+
}
|
|
38991
39096
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
38992
39097
|
throw new Error("not connected to the Jefri Chat hub (reconnecting) \u2014 try again in a moment");
|
|
38993
39098
|
this.ws.send(JSON.stringify(ev));
|
|
@@ -39143,6 +39248,11 @@ var init_src2 = __esm({
|
|
|
39143
39248
|
}
|
|
39144
39249
|
close() {
|
|
39145
39250
|
this.closing = true;
|
|
39251
|
+
if (this.recoveryEnabled) {
|
|
39252
|
+
this.registeredSocket = void 0;
|
|
39253
|
+
clearTimeout(this.recoveryRegisterTimer);
|
|
39254
|
+
this.recoveryRegisterTimer = void 0;
|
|
39255
|
+
}
|
|
39146
39256
|
this.stopPing();
|
|
39147
39257
|
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
39148
39258
|
this.reconnectTimer = void 0;
|
|
@@ -39511,6 +39621,61 @@ var init_stream_utils = __esm({
|
|
|
39511
39621
|
}
|
|
39512
39622
|
});
|
|
39513
39623
|
|
|
39624
|
+
// src/send-confirmed.ts
|
|
39625
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
39626
|
+
function sendConfirmed(client, target, text5, opts = {}) {
|
|
39627
|
+
const clientId = opts.clientId ?? randomUUID2();
|
|
39628
|
+
const timeoutMs = opts.timeoutMs ?? SEND_CONFIRM_TIMEOUT_MS;
|
|
39629
|
+
return new Promise((resolve) => {
|
|
39630
|
+
let settled = false;
|
|
39631
|
+
const finish = (out) => {
|
|
39632
|
+
if (settled) return;
|
|
39633
|
+
settled = true;
|
|
39634
|
+
clearTimeout(timer);
|
|
39635
|
+
clearInterval(linkCheck);
|
|
39636
|
+
client.off("reply_ack", onAck);
|
|
39637
|
+
client.off("error", onErr);
|
|
39638
|
+
resolve(out);
|
|
39639
|
+
};
|
|
39640
|
+
const onAck = (ev) => {
|
|
39641
|
+
if (ev?.replyId !== clientId) return;
|
|
39642
|
+
finish({ outcome: "sent", hubMessageId: String(ev.messageId ?? "") });
|
|
39643
|
+
};
|
|
39644
|
+
const onErr = (ev) => {
|
|
39645
|
+
if (ev?.replyId !== clientId) return;
|
|
39646
|
+
finish({
|
|
39647
|
+
outcome: "rejected",
|
|
39648
|
+
reason: String(ev.message ?? "the hub refused the message"),
|
|
39649
|
+
...typeof ev.code === "string" ? { code: ev.code } : {},
|
|
39650
|
+
...typeof ev.status === "number" ? { status: ev.status } : {}
|
|
39651
|
+
});
|
|
39652
|
+
};
|
|
39653
|
+
const timer = setTimeout(
|
|
39654
|
+
() => finish({ outcome: "ambiguous", reason: `no answer from the hub within ${Math.round(timeoutMs / 1e3)}s \u2014 check history before retrying` }),
|
|
39655
|
+
timeoutMs
|
|
39656
|
+
);
|
|
39657
|
+
const linkCheck = setInterval(() => {
|
|
39658
|
+
if (client.online === false) finish({ outcome: "ambiguous", reason: "the hub connection dropped before it answered \u2014 check history before retrying" });
|
|
39659
|
+
}, 250);
|
|
39660
|
+
client.on("reply_ack", onAck);
|
|
39661
|
+
client.on("error", onErr);
|
|
39662
|
+
try {
|
|
39663
|
+
if (target.groupId) client.groupMessage(target.groupId, text5, { clientId });
|
|
39664
|
+
else if (target.to) client.message(target.to, text5, { clientId });
|
|
39665
|
+
else return finish({ outcome: "rejected", reason: "need `to` or `groupId`" });
|
|
39666
|
+
} catch (e) {
|
|
39667
|
+
finish({ outcome: "rejected", reason: e?.message ?? "send failed" });
|
|
39668
|
+
}
|
|
39669
|
+
});
|
|
39670
|
+
}
|
|
39671
|
+
var SEND_CONFIRM_TIMEOUT_MS;
|
|
39672
|
+
var init_send_confirmed = __esm({
|
|
39673
|
+
"src/send-confirmed.ts"() {
|
|
39674
|
+
"use strict";
|
|
39675
|
+
SEND_CONFIRM_TIMEOUT_MS = 8e3;
|
|
39676
|
+
}
|
|
39677
|
+
});
|
|
39678
|
+
|
|
39514
39679
|
// src/hosts.ts
|
|
39515
39680
|
function formatAgentStatus(agent, body) {
|
|
39516
39681
|
const dot = body.online ? "\u{1F7E2} online" : "\u26AA offline";
|
|
@@ -39693,7 +39858,7 @@ function serverHostLabel(server2, allServers) {
|
|
|
39693
39858
|
} catch {
|
|
39694
39859
|
return safeHandle(String(server2));
|
|
39695
39860
|
}
|
|
39696
|
-
const
|
|
39861
|
+
const path17 = u.pathname.replace(/\/+$/, "");
|
|
39697
39862
|
let label = u.host;
|
|
39698
39863
|
if (allServers) {
|
|
39699
39864
|
const mine = normalizeServerUrl(String(server2));
|
|
@@ -39707,8 +39872,8 @@ function serverHostLabel(server2, allServers) {
|
|
|
39707
39872
|
}
|
|
39708
39873
|
const clash = (key) => others.some((x) => key(x) === key(u));
|
|
39709
39874
|
if (clash((x) => x.host)) {
|
|
39710
|
-
label = u.host +
|
|
39711
|
-
if (clash((x) => x.host + x.pathname.replace(/\/+$/, ""))) label = `${u.protocol}//${u.host}${
|
|
39875
|
+
label = u.host + path17;
|
|
39876
|
+
if (clash((x) => x.host + x.pathname.replace(/\/+$/, ""))) label = `${u.protocol}//${u.host}${path17}`;
|
|
39712
39877
|
}
|
|
39713
39878
|
}
|
|
39714
39879
|
return safeHandle(label);
|
|
@@ -40924,9 +41089,9 @@ var init_which = __esm({
|
|
|
40924
41089
|
// src/run-store.ts
|
|
40925
41090
|
import fs4 from "node:fs";
|
|
40926
41091
|
import np4 from "node:path";
|
|
40927
|
-
function writeAtomic(
|
|
40928
|
-
fs4.mkdirSync(np4.dirname(
|
|
40929
|
-
const tmp = `${
|
|
41092
|
+
function writeAtomic(path17, text5) {
|
|
41093
|
+
fs4.mkdirSync(np4.dirname(path17), { recursive: true });
|
|
41094
|
+
const tmp = `${path17}.tmp.${process.pid}`;
|
|
40930
41095
|
const fd = fs4.openSync(tmp, "w", 384);
|
|
40931
41096
|
try {
|
|
40932
41097
|
fs4.writeFileSync(fd, text5);
|
|
@@ -40957,10 +41122,10 @@ function writeAtomic(path15, text5) {
|
|
|
40957
41122
|
throw e;
|
|
40958
41123
|
}
|
|
40959
41124
|
fs4.closeSync(fd);
|
|
40960
|
-
fs4.renameSync(tmp,
|
|
41125
|
+
fs4.renameSync(tmp, path17);
|
|
40961
41126
|
let dfd = null;
|
|
40962
41127
|
try {
|
|
40963
|
-
dfd = fs4.openSync(np4.dirname(
|
|
41128
|
+
dfd = fs4.openSync(np4.dirname(path17), "r");
|
|
40964
41129
|
fs4.fsyncSync(dfd);
|
|
40965
41130
|
} catch (e) {
|
|
40966
41131
|
if (e?.code === "EIO" || e?.code === "ENOSPC") throw e;
|
|
@@ -40973,20 +41138,20 @@ function writeAtomic(path15, text5) {
|
|
|
40973
41138
|
}
|
|
40974
41139
|
}
|
|
40975
41140
|
}
|
|
40976
|
-
function appendJournal(
|
|
41141
|
+
function appendJournal(path17, rec) {
|
|
40977
41142
|
try {
|
|
40978
|
-
fs4.mkdirSync(np4.dirname(
|
|
40979
|
-
fs4.appendFileSync(
|
|
41143
|
+
fs4.mkdirSync(np4.dirname(path17), { recursive: true });
|
|
41144
|
+
fs4.appendFileSync(path17, JSON.stringify(rec) + "\n", { mode: 384 });
|
|
40980
41145
|
return true;
|
|
40981
41146
|
} catch {
|
|
40982
41147
|
return false;
|
|
40983
41148
|
}
|
|
40984
41149
|
}
|
|
40985
|
-
function readJournal(
|
|
41150
|
+
function readJournal(path17) {
|
|
40986
41151
|
const state = { runs: /* @__PURE__ */ new Map(), replies: /* @__PURE__ */ new Map(), malformed: 0 };
|
|
40987
41152
|
let text5 = "";
|
|
40988
41153
|
try {
|
|
40989
|
-
text5 = fs4.readFileSync(
|
|
41154
|
+
text5 = fs4.readFileSync(path17, "utf8");
|
|
40990
41155
|
} catch (e) {
|
|
40991
41156
|
if (e?.code === "ENOENT") return state;
|
|
40992
41157
|
return { ...state, readError: String(e?.message ?? e) };
|
|
@@ -41053,10 +41218,10 @@ function readJournal(path15) {
|
|
|
41053
41218
|
}
|
|
41054
41219
|
return state;
|
|
41055
41220
|
}
|
|
41056
|
-
function readMarker(
|
|
41221
|
+
function readMarker(path17) {
|
|
41057
41222
|
let text5;
|
|
41058
41223
|
try {
|
|
41059
|
-
text5 = fs4.readFileSync(
|
|
41224
|
+
text5 = fs4.readFileSync(path17, "utf8");
|
|
41060
41225
|
} catch (e) {
|
|
41061
41226
|
return e?.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable", error: String(e?.message ?? e) };
|
|
41062
41227
|
}
|
|
@@ -41068,25 +41233,25 @@ function readMarker(path15) {
|
|
|
41068
41233
|
return { kind: "unreadable", error: String(e?.message ?? e) };
|
|
41069
41234
|
}
|
|
41070
41235
|
}
|
|
41071
|
-
function writeMarker(
|
|
41072
|
-
writeAtomic(
|
|
41236
|
+
function writeMarker(path17, marker) {
|
|
41237
|
+
writeAtomic(path17, JSON.stringify(marker, null, 2));
|
|
41073
41238
|
}
|
|
41074
|
-
function removeMarker(
|
|
41075
|
-
const cur = readMarker(
|
|
41239
|
+
function removeMarker(path17, sessionId) {
|
|
41240
|
+
const cur = readMarker(path17);
|
|
41076
41241
|
if (cur.kind === "absent") return true;
|
|
41077
41242
|
if (cur.kind === "unreadable") return false;
|
|
41078
41243
|
if (cur.marker.sessionId !== sessionId) return false;
|
|
41079
41244
|
try {
|
|
41080
|
-
fs4.unlinkSync(
|
|
41245
|
+
fs4.unlinkSync(path17);
|
|
41081
41246
|
return true;
|
|
41082
41247
|
} catch (e) {
|
|
41083
41248
|
return e?.code === "ENOENT";
|
|
41084
41249
|
}
|
|
41085
41250
|
}
|
|
41086
|
-
function readHeld(
|
|
41251
|
+
function readHeld(path17) {
|
|
41087
41252
|
let text5;
|
|
41088
41253
|
try {
|
|
41089
|
-
text5 = fs4.readFileSync(
|
|
41254
|
+
text5 = fs4.readFileSync(path17, "utf8");
|
|
41090
41255
|
} catch (e) {
|
|
41091
41256
|
return e?.code === "ENOENT" ? { ok: true, held: { replyIds: {} } } : { ok: false, error: String(e?.message ?? e) };
|
|
41092
41257
|
}
|
|
@@ -41098,11 +41263,11 @@ function readHeld(path15) {
|
|
|
41098
41263
|
return { ok: false, error: String(e?.message ?? e) };
|
|
41099
41264
|
}
|
|
41100
41265
|
}
|
|
41101
|
-
function writeHeld(
|
|
41102
|
-
writeAtomic(
|
|
41266
|
+
function writeHeld(path17, held) {
|
|
41267
|
+
writeAtomic(path17, JSON.stringify(held, null, 2));
|
|
41103
41268
|
}
|
|
41104
|
-
function writeAutoConfigFile(
|
|
41105
|
-
writeAtomic(
|
|
41269
|
+
function writeAutoConfigFile(path17, cfg2) {
|
|
41270
|
+
writeAtomic(path17, JSON.stringify(cfg2, null, 2));
|
|
41106
41271
|
}
|
|
41107
41272
|
function pidAlive(pid) {
|
|
41108
41273
|
if (!pid) return false;
|
|
@@ -41293,34 +41458,34 @@ var init_responder_lock = __esm({
|
|
|
41293
41458
|
// src/config-mutex.ts
|
|
41294
41459
|
import fs6 from "node:fs";
|
|
41295
41460
|
import np6 from "node:path";
|
|
41296
|
-
function openMutex(
|
|
41297
|
-
const cached2 = dbs.get(
|
|
41461
|
+
function openMutex(path17) {
|
|
41462
|
+
const cached2 = dbs.get(path17);
|
|
41298
41463
|
if (cached2) return cached2;
|
|
41299
41464
|
const mod = loadSqliteModule();
|
|
41300
|
-
if (!mod) throw new ConfigMutexError(
|
|
41465
|
+
if (!mod) throw new ConfigMutexError(path17, new Error(sqliteAvailability().ok ? "node:sqlite unavailable" : sqliteAvailability().reason));
|
|
41301
41466
|
try {
|
|
41302
|
-
fs6.mkdirSync(np6.dirname(
|
|
41303
|
-
const db = new mod.DatabaseSync(`${
|
|
41467
|
+
fs6.mkdirSync(np6.dirname(path17), { recursive: true });
|
|
41468
|
+
const db = new mod.DatabaseSync(`${path17}.mutex.db`);
|
|
41304
41469
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
41305
41470
|
db.exec("CREATE TABLE IF NOT EXISTS mutex (id INTEGER PRIMARY KEY CHECK (id = 1), holder INTEGER NOT NULL, at INTEGER NOT NULL)");
|
|
41306
|
-
dbs.set(
|
|
41471
|
+
dbs.set(path17, db);
|
|
41307
41472
|
return db;
|
|
41308
41473
|
} catch (e) {
|
|
41309
|
-
throw new ConfigMutexError(
|
|
41474
|
+
throw new ConfigMutexError(path17, e);
|
|
41310
41475
|
}
|
|
41311
41476
|
}
|
|
41312
|
-
function withConfigMutex(
|
|
41477
|
+
function withConfigMutex(path17, fn) {
|
|
41313
41478
|
if (depth > 0) return fn();
|
|
41314
|
-
const db = openMutex(
|
|
41479
|
+
const db = openMutex(path17);
|
|
41315
41480
|
try {
|
|
41316
41481
|
db.exec("BEGIN IMMEDIATE");
|
|
41317
41482
|
} catch (e) {
|
|
41318
|
-
dbs.delete(
|
|
41483
|
+
dbs.delete(path17);
|
|
41319
41484
|
try {
|
|
41320
41485
|
db.close();
|
|
41321
41486
|
} catch {
|
|
41322
41487
|
}
|
|
41323
|
-
throw new ConfigMutexError(
|
|
41488
|
+
throw new ConfigMutexError(path17, e);
|
|
41324
41489
|
}
|
|
41325
41490
|
depth++;
|
|
41326
41491
|
try {
|
|
@@ -41345,8 +41510,8 @@ var init_config_mutex = __esm({
|
|
|
41345
41510
|
init_responder_lock();
|
|
41346
41511
|
dbs = /* @__PURE__ */ new Map();
|
|
41347
41512
|
ConfigMutexError = class extends Error {
|
|
41348
|
-
constructor(
|
|
41349
|
-
super(`the config write mutex ${
|
|
41513
|
+
constructor(path17, cause) {
|
|
41514
|
+
super(`the config write mutex ${path17}.mutex.db cannot be taken (${cause?.message ?? cause}) \u2014 the write was NOT performed, it would not have been serialized`);
|
|
41350
41515
|
this.name = "ConfigMutexError";
|
|
41351
41516
|
}
|
|
41352
41517
|
};
|
|
@@ -41355,7 +41520,7 @@ var init_config_mutex = __esm({
|
|
|
41355
41520
|
});
|
|
41356
41521
|
|
|
41357
41522
|
// src/run-reporter.ts
|
|
41358
|
-
import { randomUUID as
|
|
41523
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
41359
41524
|
import np7 from "node:path";
|
|
41360
41525
|
var RUN_HEARTBEAT_MS, safeUser, RunReporter;
|
|
41361
41526
|
var init_run_reporter = __esm({
|
|
@@ -41374,7 +41539,7 @@ var init_run_reporter = __esm({
|
|
|
41374
41539
|
this.replyDeadlineMs = deps.replyDeadlineMs ?? REPLY_DEADLINE_MS;
|
|
41375
41540
|
}
|
|
41376
41541
|
deps;
|
|
41377
|
-
sessionId =
|
|
41542
|
+
sessionId = randomUUID3();
|
|
41378
41543
|
runs = /* @__PURE__ */ new Map();
|
|
41379
41544
|
replies = /* @__PURE__ */ new Map();
|
|
41380
41545
|
/** Unacknowledged run frames, keyed `${runId}:${seq}`, in send order. */
|
|
@@ -41535,7 +41700,7 @@ var init_run_reporter = __esm({
|
|
|
41535
41700
|
/** Mint the id and journal the run BEFORE the caller spawns anything. */
|
|
41536
41701
|
startRun(meta) {
|
|
41537
41702
|
if (!this.started) throw new Error("run reporter not started \u2014 the session marker must be established before any autonomous work");
|
|
41538
|
-
const runId =
|
|
41703
|
+
const runId = randomUUID3();
|
|
41539
41704
|
const startedAt = this.now();
|
|
41540
41705
|
const st = { runId, seq: 0, meta, startedAt, epoch: this.deps.workEpoch(), durable: true, ended: false };
|
|
41541
41706
|
const seq = this.nextSeq(st);
|
|
@@ -41621,7 +41786,7 @@ var init_run_reporter = __esm({
|
|
|
41621
41786
|
* finalization verdict, or the cancel path. `onSettled` fires exactly once. */
|
|
41622
41787
|
queueReply(runId, target, text5, onSettled) {
|
|
41623
41788
|
if (!this.started) throw new Error("run reporter not started");
|
|
41624
|
-
const replyId =
|
|
41789
|
+
const replyId = randomUUID3();
|
|
41625
41790
|
const createdAt = this.now();
|
|
41626
41791
|
const epoch = this.deps.workEpoch();
|
|
41627
41792
|
const groupGen = target.groupId ? this.deps.groupGenOf?.(target.groupId) : void 0;
|
|
@@ -44064,9 +44229,9 @@ var init_autonomous = __esm({
|
|
|
44064
44229
|
persistFailureForTest = null;
|
|
44065
44230
|
cfg = { ...DEFAULTS2 };
|
|
44066
44231
|
AutoConfigWriteError = class extends Error {
|
|
44067
|
-
constructor(
|
|
44068
|
-
super(whileDisabling ? `durable cancellation failed: could not write ${
|
|
44069
|
-
this.path =
|
|
44232
|
+
constructor(path17, cause, whileDisabling) {
|
|
44233
|
+
super(whileDisabling ? `durable cancellation failed: could not write ${path17} (${cause?.message ?? cause}). Autonomy is OFF in this process and its work was stopped, but the setting could not be saved \u2014 the next start may still read the old config.` : `could not save the autonomy config to ${path17} (${cause?.message ?? cause})`);
|
|
44234
|
+
this.path = path17;
|
|
44070
44235
|
this.cause = cause;
|
|
44071
44236
|
this.whileDisabling = whileDisabling;
|
|
44072
44237
|
this.name = "AutoConfigWriteError";
|
|
@@ -44535,8 +44700,8 @@ async function mintFileLink(hub, token, fileId) {
|
|
|
44535
44700
|
clearTimeout(killer);
|
|
44536
44701
|
}
|
|
44537
44702
|
}
|
|
44538
|
-
async function remoteUploadCommand(c2, ctx, target,
|
|
44539
|
-
const fname = crossBasename(
|
|
44703
|
+
async function remoteUploadCommand(c2, ctx, target, path17, caption, where, signal) {
|
|
44704
|
+
const fname = crossBasename(path17);
|
|
44540
44705
|
const fmime = mimeOf(fname);
|
|
44541
44706
|
const hub = ctx.serverUrl.replace(/\/$/, "");
|
|
44542
44707
|
try {
|
|
@@ -44595,10 +44760,10 @@ ${uploadUrl}
|
|
|
44595
44760
|
|
|
44596
44761
|
\u{1F4BB} Or run this ONE command on a machine that HAS the file and has internet (the user's terminal \u2014 NOT a sandboxed assistant environment without egress):
|
|
44597
44762
|
|
|
44598
|
-
curl -sS -X POST ${shq(uploadUrl)} -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(
|
|
44763
|
+
curl -sS -X POST ${shq(uploadUrl)} -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(path17)}
|
|
44599
44764
|
|
|
44600
44765
|
Windows PowerShell instead:
|
|
44601
|
-
Invoke-RestMethod -Uri ${psq(uploadUrl)} -Method Post -ContentType ${psq(fmime)} -InFile ${psPathArg(
|
|
44766
|
+
Invoke-RestMethod -Uri ${psq(uploadUrl)} -Method Post -ContentType ${psq(fmime)} -InFile ${psPathArg(path17)}
|
|
44602
44767
|
|
|
44603
44768
|
The link is single-use and expires in ${mins} min; the file name and caption are already attached to it. Run it now, then confirm delivery from its JSON response ({"ok":true,...}).
|
|
44604
44769
|
\u26A0\uFE0F If YOU are a sandboxed assistant with no network egress and the file lives in YOUR sandbox: this command cannot run anywhere that can see the file. Instead \u2014 if the file is 256KB or less, call this tool again with dataUrl (base64 it) + fileName; if larger, PRESENT the file to the user for download and tell them to open the LINK ABOVE and pick the downloaded file. Do NOT paste this curl into your own shell.
|
|
@@ -44610,7 +44775,7 @@ Never base64 anything over 256KB through dataUrl \u2014 that is minutes of model
|
|
|
44610
44775
|
const web = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
|
|
44611
44776
|
const dropLink = target.to ? `${web}/?to=${encodeURIComponent(target.to)}` : `${web}/?group=${encodeURIComponent(target.groupId)}`;
|
|
44612
44777
|
const q = (target.to ? `to=${encodeURIComponent(target.to)}` : `groupId=${encodeURIComponent(target.groupId)}`) + `&fileName=${encodeURIComponent(fname)}` + (caption ? `&caption=${encodeURIComponent(caption)}` : "");
|
|
44613
|
-
const cmd = `curl -s -X POST ${shq(`${hub}/api/files?${q}`)} -H "Authorization: Bearer $JEFRI_TOKEN" -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(
|
|
44778
|
+
const cmd = `curl -s -X POST ${shq(`${hub}/api/files?${q}`)} -H "Authorization: Bearer $JEFRI_TOKEN" -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(path17)}`;
|
|
44614
44779
|
return {
|
|
44615
44780
|
fail: `This is the cloud connector, so it can't read "${fname}" from that path, and the upload-link service didn't answer. Options:
|
|
44616
44781
|
|
|
@@ -44881,12 +45046,16 @@ async function uploadFileHttp(c2, serverUrl, target, fileName, mime, bytes, capt
|
|
|
44881
45046
|
if (caption) params.set("caption", caption);
|
|
44882
45047
|
const headers = { Authorization: `Bearer ${c2.token}`, "Content-Type": mime };
|
|
44883
45048
|
if (idem) headers["idempotency-key"] = idem;
|
|
45049
|
+
const desktop = !isStateless(c2) && c2.registeredRecoveryEnabled;
|
|
44884
45050
|
let res;
|
|
44885
45051
|
try {
|
|
44886
45052
|
res = await fetch(`${hub}/api/files?${params.toString()}`, {
|
|
44887
45053
|
method: "POST",
|
|
44888
45054
|
headers,
|
|
44889
|
-
body: Buffer.from(bytes)
|
|
45055
|
+
body: Buffer.from(bytes),
|
|
45056
|
+
// Same payload-scaled allowance as the SDK upload path. This covers the
|
|
45057
|
+
// response body too; a silent hub must not hang this Desktop tool forever.
|
|
45058
|
+
...desktop ? { signal: AbortSignal.timeout(Math.ceil(3e4 + bytes.length / 20)) } : {}
|
|
44890
45059
|
});
|
|
44891
45060
|
} catch (e) {
|
|
44892
45061
|
const err = new Error(`upload failed: ${e?.message ?? e}`);
|
|
@@ -44899,6 +45068,10 @@ async function uploadFileHttp(c2, serverUrl, target, fileName, mime, bytes, capt
|
|
|
44899
45068
|
if (res.status >= 500) err.jefriAmbiguous = true;
|
|
44900
45069
|
throw err;
|
|
44901
45070
|
}
|
|
45071
|
+
if (desktop) {
|
|
45072
|
+
const receipt = await res.json().catch(() => null);
|
|
45073
|
+
if (receipt?.ok !== true) throw new Error("upload response did not confirm delivery");
|
|
45074
|
+
}
|
|
44902
45075
|
}
|
|
44903
45076
|
function defaultCaps(local) {
|
|
44904
45077
|
return local ? [CAP.LOCAL_FS, CAP.PERSISTENT] : [CAP.REMOTE_SANDBOX, CAP.AUTONOMY, CAP.SEARCH];
|
|
@@ -45262,9 +45435,12 @@ ${e?.message ?? e}`);
|
|
|
45262
45435
|
async () => {
|
|
45263
45436
|
const unbound = await ctx.identityStatus?.();
|
|
45264
45437
|
if (unbound) return ok3(unbound);
|
|
45265
|
-
return withClient(
|
|
45266
|
-
|
|
45267
|
-
|
|
45438
|
+
return withClient(async (c2) => {
|
|
45439
|
+
const desktop = !isStateless(c2) && c2.registeredRecoveryEnabled;
|
|
45440
|
+
const status = desktop ? c2.connectionStatus : "online";
|
|
45441
|
+
const reason = desktop && c2.terminalCode ? ` Connection stopped: ${c2.terminalCode}; automatic reconnection is disabled.` : "";
|
|
45442
|
+
return ok3(`You are "${c2.identity.displayName}" (@${safeHandle(c2.identity.username)}) on Jefri Chat at ${ctx.serverUrl}, status: ${status}.${reason}`);
|
|
45443
|
+
});
|
|
45268
45444
|
}
|
|
45269
45445
|
);
|
|
45270
45446
|
server2.registerTool(
|
|
@@ -45303,8 +45479,8 @@ ${e?.message ?? e}`);
|
|
|
45303
45479
|
})
|
|
45304
45480
|
);
|
|
45305
45481
|
const NOT_OWNER = "This only works when you run the Jefri connector as your OWN (human) account and own this agent. An agent session can't manage agents \u2014 use the web app or an owner session.";
|
|
45306
|
-
const ownerApi = async (c2,
|
|
45307
|
-
const res = await fetch(`${ctx.serverUrl}${
|
|
45482
|
+
const ownerApi = async (c2, path17, method) => {
|
|
45483
|
+
const res = await fetch(`${ctx.serverUrl}${path17}`, {
|
|
45308
45484
|
method,
|
|
45309
45485
|
headers: { Authorization: `Bearer ${c2.token}` }
|
|
45310
45486
|
});
|
|
@@ -45407,6 +45583,12 @@ Use jefri_agent_apps with agent=<name> to see its live app + active state.`,
|
|
|
45407
45583
|
return failAmbiguous(`Message send failed: ${e?.message ?? e}.`);
|
|
45408
45584
|
}
|
|
45409
45585
|
}
|
|
45586
|
+
if (c2.registeredRecoveryEnabled) {
|
|
45587
|
+
const sent = await sendConfirmed(c2, { to }, text5);
|
|
45588
|
+
if (sent.outcome === "rejected") return fail(`Message rejected: ${sent.reason}`);
|
|
45589
|
+
if (sent.outcome === "ambiguous") return failAmbiguous(sent.reason);
|
|
45590
|
+
return ok3(`Sent to @${safeHandle(to)}: "${text5}"`);
|
|
45591
|
+
}
|
|
45410
45592
|
const r = await confirmWrite(c2, () => c2.message(to, text5));
|
|
45411
45593
|
if (r.error) return fail(`Message rejected: ${r.error}`);
|
|
45412
45594
|
return ok3(`Sent to @${safeHandle(to)}: "${text5}"`);
|
|
@@ -45457,18 +45639,18 @@ ${fp}`);
|
|
|
45457
45639
|
caption: external_exports.string().optional().describe("optional private E2E caption sent as a separate encrypted message")
|
|
45458
45640
|
}
|
|
45459
45641
|
},
|
|
45460
|
-
async ({ to, path:
|
|
45642
|
+
async ({ to, path: path17, dataUrl, fileName, caption }) => withClient(async (c2) => {
|
|
45461
45643
|
let name, mime, url;
|
|
45462
|
-
if (
|
|
45644
|
+
if (path17) {
|
|
45463
45645
|
if (!ctx.local) {
|
|
45464
45646
|
return fail(
|
|
45465
45647
|
`I can't read local files from the cloud connector. Use a local/stdin Jefri Chat connector, or open the web chat and drop the file there.`
|
|
45466
45648
|
);
|
|
45467
45649
|
}
|
|
45468
|
-
const abs = resolveLocalFile(resolveUserPath(
|
|
45650
|
+
const abs = resolveLocalFile(resolveUserPath(path17));
|
|
45469
45651
|
if (!abs) {
|
|
45470
45652
|
return fail(
|
|
45471
|
-
`I can't find a file at ${
|
|
45653
|
+
`I can't find a file at ${path17} on this machine.`
|
|
45472
45654
|
);
|
|
45473
45655
|
}
|
|
45474
45656
|
const buf = fs8.readFileSync(abs);
|
|
@@ -45520,18 +45702,18 @@ ${fp}`);
|
|
|
45520
45702
|
caption: external_exports.string().optional().describe("optional private E2E group caption sent separately")
|
|
45521
45703
|
}
|
|
45522
45704
|
},
|
|
45523
|
-
async ({ groupId, path:
|
|
45705
|
+
async ({ groupId, path: path17, dataUrl, fileName, caption }) => withClient(async (c2) => {
|
|
45524
45706
|
let name, mime, url;
|
|
45525
|
-
if (
|
|
45707
|
+
if (path17) {
|
|
45526
45708
|
if (!ctx.local) {
|
|
45527
45709
|
return fail(
|
|
45528
45710
|
`I can't read local files from the cloud connector. Use a local/stdin Jefri Chat connector, or open the web chat and drop the file there.`
|
|
45529
45711
|
);
|
|
45530
45712
|
}
|
|
45531
|
-
const abs = resolveLocalFile(resolveUserPath(
|
|
45713
|
+
const abs = resolveLocalFile(resolveUserPath(path17));
|
|
45532
45714
|
if (!abs) {
|
|
45533
45715
|
return fail(
|
|
45534
|
-
`I can't find a file at ${
|
|
45716
|
+
`I can't find a file at ${path17} on this machine.`
|
|
45535
45717
|
);
|
|
45536
45718
|
}
|
|
45537
45719
|
const buf = fs8.readFileSync(abs);
|
|
@@ -45734,17 +45916,17 @@ ${lines.join("\n")}`
|
|
|
45734
45916
|
fileName: external_exports.string().optional().describe("file name (used with dataUrl, or to name a fileUrl download)")
|
|
45735
45917
|
}
|
|
45736
45918
|
},
|
|
45737
|
-
async ({ to, path:
|
|
45919
|
+
async ({ to, path: path17, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c2) => {
|
|
45738
45920
|
let name, mime, url;
|
|
45739
45921
|
let inlineBytes = null;
|
|
45740
|
-
if (
|
|
45922
|
+
if (path17) {
|
|
45741
45923
|
if (!ctx.local) {
|
|
45742
|
-
const r = await remoteUploadCommand(c2, ctx, { to },
|
|
45924
|
+
const r = await remoteUploadCommand(c2, ctx, { to }, path17, caption, `@${safeHandle(to)}`, extra?.signal);
|
|
45743
45925
|
return "ok" in r ? ok3(r.ok) : fail(r.fail);
|
|
45744
45926
|
}
|
|
45745
|
-
const abs = resolveLocalFile(resolveUserPath(
|
|
45927
|
+
const abs = resolveLocalFile(resolveUserPath(path17));
|
|
45746
45928
|
if (!abs) {
|
|
45747
|
-
return fail(`I can't find a file at ${
|
|
45929
|
+
return fail(`I can't find a file at ${path17} on this machine.`);
|
|
45748
45930
|
}
|
|
45749
45931
|
const buf = fs8.readFileSync(abs);
|
|
45750
45932
|
name = np9.basename(abs);
|
|
@@ -45778,7 +45960,7 @@ ${lines.join("\n")}`
|
|
|
45778
45960
|
name,
|
|
45779
45961
|
mime,
|
|
45780
45962
|
rawBytes,
|
|
45781
|
-
retryKey(extra, fileSendIdemParts(to, { path:
|
|
45963
|
+
retryKey(extra, fileSendIdemParts(to, { path: path17, fileUrl, fileName }, name))
|
|
45782
45964
|
);
|
|
45783
45965
|
if (res?.deduplicated)
|
|
45784
45966
|
return ok3(`Already delivered \u2014 ${name} matched a recent identical upload and was suppressed as a retry (round 28 #2: suppression is SAID, never a false "Sent"). If you meant a new copy, send it again.`);
|
|
@@ -45787,8 +45969,8 @@ ${lines.join("\n")}`
|
|
|
45787
45969
|
}
|
|
45788
45970
|
return ok3(`Sent \u{1F4CE} ${name} (${MB(rawBytes.length)} MB) to @${safeHandle(to)}${caption ? ` \u2014 "${caption}"` : ""}`);
|
|
45789
45971
|
}
|
|
45790
|
-
const fileIdem = retryKey(extra, fileSendIdemParts(to, { path:
|
|
45791
|
-
if (rawBytes.length > WS_SAFE_FILE_BYTES || fileIdem) {
|
|
45972
|
+
const fileIdem = retryKey(extra, fileSendIdemParts(to, { path: path17, fileUrl, fileName }, name));
|
|
45973
|
+
if (c2.registeredRecoveryEnabled || rawBytes.length > WS_SAFE_FILE_BYTES || fileIdem) {
|
|
45792
45974
|
try {
|
|
45793
45975
|
await uploadFileHttp(c2, ctx.serverUrl, { to }, name, mime, rawBytes, caption, fileIdem);
|
|
45794
45976
|
} catch (e2) {
|
|
@@ -45839,7 +46021,7 @@ ${lines.join("\n")}`
|
|
|
45839
46021
|
return ok3(`Sent \u{1F4C4} ${fileName} (${MB(contentBytes.length)} MB) to @${safeHandle(to)}${caption ? ` \u2014 "${caption}"` : ""}`);
|
|
45840
46022
|
}
|
|
45841
46023
|
const textIdem = retryKey(extra, ["file", to, fileName, createHash2("sha256").update(contentBytes).digest("hex")]);
|
|
45842
|
-
if (contentBytes.length > WS_SAFE_FILE_BYTES || textIdem) {
|
|
46024
|
+
if (c2.registeredRecoveryEnabled || contentBytes.length > WS_SAFE_FILE_BYTES || textIdem) {
|
|
45843
46025
|
try {
|
|
45844
46026
|
await uploadFileHttp(c2, ctx.serverUrl, { to }, fileName, mime, contentBytes, caption, textIdem);
|
|
45845
46027
|
} catch (e2) {
|
|
@@ -45867,12 +46049,12 @@ ${lines.join("\n")}`
|
|
|
45867
46049
|
caption: external_exports.string().optional().describe("optional text caption to send with it")
|
|
45868
46050
|
}
|
|
45869
46051
|
},
|
|
45870
|
-
async ({ to, path:
|
|
46052
|
+
async ({ to, path: path17, caption }, extra) => withClient(async (c2) => {
|
|
45871
46053
|
if (!ctx.local)
|
|
45872
46054
|
return fail(
|
|
45873
46055
|
`Sending a folder reads files off disk, which this connector can't do \u2014 it runs in the cloud. Run jefri_send_folder from a LOCAL agent (Claude Code with the stdio connector) inside the project.`
|
|
45874
46056
|
);
|
|
45875
|
-
const rawDir =
|
|
46057
|
+
const rawDir = path17 && path17.trim() ? path17 : ctx.cwd ?? process.cwd();
|
|
45876
46058
|
const dir = resolveUserPath(rawDir);
|
|
45877
46059
|
if (!fs8.existsSync(dir))
|
|
45878
46060
|
return fail(
|
|
@@ -46245,11 +46427,11 @@ ${cmd}
|
|
|
46245
46427
|
kind: external_exports.enum(["file", "contract", "skill", "note"]).optional()
|
|
46246
46428
|
}
|
|
46247
46429
|
},
|
|
46248
|
-
async ({ departmentId, path:
|
|
46249
|
-
const fname = np9.basename(
|
|
46430
|
+
async ({ departmentId, path: path17, kind }) => withClient(async (c2) => {
|
|
46431
|
+
const fname = np9.basename(path17.startsWith("~") ? path17.slice(1) : path17);
|
|
46250
46432
|
const mime = mimeOf(fname);
|
|
46251
46433
|
const q = `name=${encodeURIComponent(fname)}&kind=${kind ?? "file"}`;
|
|
46252
|
-
const cmd = `curl -s -X POST ${shq(`${hubBase()}/api/departments/${departmentId}/files?${q}`)} -H "Authorization: Bearer $JEFRI_TOKEN" -H "Content-Type: ${mime}" --data-binary @${shq(
|
|
46434
|
+
const cmd = `curl -s -X POST ${shq(`${hubBase()}/api/departments/${departmentId}/files?${q}`)} -H "Authorization: Bearer $JEFRI_TOKEN" -H "Content-Type: ${mime}" --data-binary @${shq(path17)}`;
|
|
46253
46435
|
return ok3(`To add ${fname} to the department, run in the terminal:
|
|
46254
46436
|
|
|
46255
46437
|
${cmd}
|
|
@@ -46331,10 +46513,10 @@ ${cmd}
|
|
|
46331
46513
|
description: "Add a local file to this agent's document memory. Memory is CURATED BY THE OWNER, so this returns a command that uses the OWNER token (the agent token can't add docs) \u2014 or the owner can upload it from the agent's page in the web app. Documents follow the agent's visibility (public \u2192 owner's connections can read; shared \u2192 chosen people; private \u2192 owner only). Warn before uploading anything sensitive to a public agent.",
|
|
46332
46514
|
inputSchema: { path: external_exports.string().describe("local file path to upload") }
|
|
46333
46515
|
},
|
|
46334
|
-
async ({ path:
|
|
46335
|
-
const fname = np9.basename(
|
|
46516
|
+
async ({ path: path17 }) => withClient(async (c2) => {
|
|
46517
|
+
const fname = np9.basename(path17.startsWith("~") ? path17.slice(1) : path17);
|
|
46336
46518
|
const mime = mimeOf(fname);
|
|
46337
|
-
const cmd = `curl -s -X POST ${shq(`${hubBase()}/api/agents/${encodeURIComponent(c2.identity.username)}/docs?name=${encodeURIComponent(fname)}`)} -H "Authorization: Bearer $JEFRI_OWNER_TOKEN" -H "Content-Type: ${mime}" --data-binary @${shq(
|
|
46519
|
+
const cmd = `curl -s -X POST ${shq(`${hubBase()}/api/agents/${encodeURIComponent(c2.identity.username)}/docs?name=${encodeURIComponent(fname)}`)} -H "Authorization: Bearer $JEFRI_OWNER_TOKEN" -H "Content-Type: ${mime}" --data-binary @${shq(path17)}`;
|
|
46338
46520
|
return ok3(
|
|
46339
46521
|
`Adding to agent memory is OWNER-only, so this uses your owner token. Set it first (export JEFRI_OWNER_TOKEN=\u2026 from the app), then run:
|
|
46340
46522
|
|
|
@@ -46538,6 +46720,12 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
46538
46720
|
return failAmbiguous(`Group post failed: ${e?.message ?? e}.`);
|
|
46539
46721
|
}
|
|
46540
46722
|
}
|
|
46723
|
+
if (c2.registeredRecoveryEnabled) {
|
|
46724
|
+
const sent = await sendConfirmed(c2, { groupId }, text5);
|
|
46725
|
+
if (sent.outcome === "rejected") return fail(`Couldn't post to the group: ${sent.reason}`);
|
|
46726
|
+
if (sent.outcome === "ambiguous") return failAmbiguous(sent.reason);
|
|
46727
|
+
return ok3("Posted to the group.");
|
|
46728
|
+
}
|
|
46541
46729
|
const r = await confirmWrite(c2, () => c2.groupMessage(groupId, text5));
|
|
46542
46730
|
if (r.error) return fail(`Couldn't post to the group: ${r.error}`);
|
|
46543
46731
|
return ok3(`Posted to the group.`);
|
|
@@ -46557,16 +46745,16 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
46557
46745
|
fileName: external_exports.string().optional().describe("file name (used with dataUrl, or to name a fileUrl download)")
|
|
46558
46746
|
}
|
|
46559
46747
|
},
|
|
46560
|
-
async ({ groupId, path:
|
|
46748
|
+
async ({ groupId, path: path17, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c2) => {
|
|
46561
46749
|
let name, mime, url;
|
|
46562
46750
|
let inlineBytes = null;
|
|
46563
|
-
if (
|
|
46751
|
+
if (path17) {
|
|
46564
46752
|
if (!ctx.local) {
|
|
46565
|
-
const r = await remoteUploadCommand(c2, ctx, { groupId },
|
|
46753
|
+
const r = await remoteUploadCommand(c2, ctx, { groupId }, path17, caption, "the group", extra?.signal);
|
|
46566
46754
|
return "ok" in r ? ok3(r.ok) : fail(r.fail);
|
|
46567
46755
|
}
|
|
46568
|
-
const abs = resolveLocalFile(resolveUserPath(
|
|
46569
|
-
if (!abs) return fail(`I can't find a file at ${
|
|
46756
|
+
const abs = resolveLocalFile(resolveUserPath(path17));
|
|
46757
|
+
if (!abs) return fail(`I can't find a file at ${path17} on this machine.`);
|
|
46570
46758
|
const buf = fs8.readFileSync(abs);
|
|
46571
46759
|
name = np9.basename(abs);
|
|
46572
46760
|
mime = mimeOf(name);
|
|
@@ -46599,7 +46787,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
46599
46787
|
name,
|
|
46600
46788
|
mime,
|
|
46601
46789
|
rawBytes,
|
|
46602
|
-
retryKey(extra, fileSendIdemParts(groupId, { path:
|
|
46790
|
+
retryKey(extra, fileSendIdemParts(groupId, { path: path17, fileUrl, fileName }, name))
|
|
46603
46791
|
);
|
|
46604
46792
|
if (res?.deduplicated)
|
|
46605
46793
|
return ok3(`Already delivered \u2014 ${name} matched a recent identical upload and was suppressed as a retry. If you meant a new copy, send it again.`);
|
|
@@ -46608,8 +46796,8 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
46608
46796
|
}
|
|
46609
46797
|
return ok3(`Sent \u{1F4CE} ${name} (${MB(rawBytes.length)} MB) to the group.`);
|
|
46610
46798
|
}
|
|
46611
|
-
const fileIdem = retryKey(extra, fileSendIdemParts(groupId, { path:
|
|
46612
|
-
if (rawBytes.length > WS_SAFE_FILE_BYTES || fileIdem) {
|
|
46799
|
+
const fileIdem = retryKey(extra, fileSendIdemParts(groupId, { path: path17, fileUrl, fileName }, name));
|
|
46800
|
+
if (c2.registeredRecoveryEnabled || rawBytes.length > WS_SAFE_FILE_BYTES || fileIdem) {
|
|
46613
46801
|
try {
|
|
46614
46802
|
await uploadFileHttp(c2, ctx.serverUrl, { groupId }, name, mime, rawBytes, caption, fileIdem);
|
|
46615
46803
|
} catch (e2) {
|
|
@@ -47356,6 +47544,7 @@ var init_tools = __esm({
|
|
|
47356
47544
|
"use strict";
|
|
47357
47545
|
init_zod();
|
|
47358
47546
|
init_stream_utils();
|
|
47547
|
+
init_send_confirmed();
|
|
47359
47548
|
init_hosts();
|
|
47360
47549
|
init_message_history();
|
|
47361
47550
|
init_src();
|
|
@@ -47586,7 +47775,7 @@ ${notice}` : text5 },
|
|
|
47586
47775
|
});
|
|
47587
47776
|
|
|
47588
47777
|
// src/runtime-identity.ts
|
|
47589
|
-
import { randomBytes as randomBytes3, randomUUID as
|
|
47778
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID4 } from "node:crypto";
|
|
47590
47779
|
function runtimeAnnouncement() {
|
|
47591
47780
|
return { ...RUNTIME };
|
|
47592
47781
|
}
|
|
@@ -47595,7 +47784,7 @@ var init_runtime_identity = __esm({
|
|
|
47595
47784
|
"src/runtime-identity.ts"() {
|
|
47596
47785
|
"use strict";
|
|
47597
47786
|
RUNTIME = Object.freeze({
|
|
47598
|
-
id:
|
|
47787
|
+
id: randomUUID4(),
|
|
47599
47788
|
proof: randomBytes3(32).toString("hex"),
|
|
47600
47789
|
pid: process.pid,
|
|
47601
47790
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -47995,10 +48184,10 @@ function ensureDirs() {
|
|
|
47995
48184
|
}
|
|
47996
48185
|
}
|
|
47997
48186
|
function publishEndpoint(d, deps = {}) {
|
|
47998
|
-
const writeFile = deps.writeFileSync ?? ((
|
|
48187
|
+
const writeFile = deps.writeFileSync ?? ((path17, data, opts) => fs10.writeFileSync(path17, data, opts));
|
|
47999
48188
|
const rename = deps.renameSync ?? ((from, to) => fs10.renameSync(from, to));
|
|
48000
|
-
const unlink = deps.unlinkSync ?? ((
|
|
48001
|
-
const readFile = deps.readFileSync ?? ((
|
|
48189
|
+
const unlink = deps.unlinkSync ?? ((path17) => fs10.unlinkSync(path17));
|
|
48190
|
+
const readFile = deps.readFileSync ?? ((path17) => fs10.readFileSync(path17, "utf8"));
|
|
48002
48191
|
ensureDirs();
|
|
48003
48192
|
if (!d.procToken) d = { ...d, procToken: procToken(d.pid) };
|
|
48004
48193
|
if (!d.nonce) d = { ...d, nonce: INSTANCE_NONCE };
|
|
@@ -49346,10 +49535,10 @@ data: ${JSON.stringify(data)}
|
|
|
49346
49535
|
if (!isLoopback(req.socket.remoteAddress)) return send(res, 403, "forbidden");
|
|
49347
49536
|
if (!hostOk(req)) return send(res, 421, "bad host");
|
|
49348
49537
|
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
49349
|
-
const
|
|
49538
|
+
const path17 = url.pathname;
|
|
49350
49539
|
const method = req.method ?? "GET";
|
|
49351
|
-
if (method === "GET" &&
|
|
49352
|
-
const nonce =
|
|
49540
|
+
if (method === "GET" && path17.startsWith("/launch/")) {
|
|
49541
|
+
const nonce = path17.slice("/launch/".length);
|
|
49353
49542
|
if (!launchNonce || !safeEqual(nonce, launchNonce)) return send(res, 404, "not found");
|
|
49354
49543
|
launchNonce = null;
|
|
49355
49544
|
sessionExpiry = Date.now() + ttlMs;
|
|
@@ -49363,15 +49552,15 @@ data: ${JSON.stringify(data)}
|
|
|
49363
49552
|
return res.end();
|
|
49364
49553
|
}
|
|
49365
49554
|
if (!authed(req)) return send(res, 401, "unauthorized");
|
|
49366
|
-
if (method === "GET" &&
|
|
49367
|
-
if (method === "GET" &&
|
|
49368
|
-
if (method === "GET" &&
|
|
49369
|
-
if (method === "GET" &&
|
|
49555
|
+
if (method === "GET" && path17 === "/") return send(res, 200, pageHtml2, "text/html; charset=utf-8");
|
|
49556
|
+
if (method === "GET" && path17 === "/app.js") return send(res, 200, appJs, "application/javascript; charset=utf-8");
|
|
49557
|
+
if (method === "GET" && path17 === "/app.css") return send(res, 200, appCss, "text/css; charset=utf-8");
|
|
49558
|
+
if (method === "GET" && path17 === "/api/inbox") {
|
|
49370
49559
|
const selected = opts.selectKey && records.has(opts.selectKey) ? opts.selectKey : null;
|
|
49371
49560
|
const body = { messages: snapshot2().map(publishRow), csrf: csrfToken, snapshotSeq: seq, agents: withDisplay(agentsSnapshot), selected, expiresAt: sessionExpiry, drafts: { durable: !!drafts?.durable } };
|
|
49372
49561
|
return send(res, 200, JSON.stringify(body), "application/json");
|
|
49373
49562
|
}
|
|
49374
|
-
if (method === "GET" &&
|
|
49563
|
+
if (method === "GET" && path17 === "/api/message") {
|
|
49375
49564
|
const key = url.searchParams.get("key") ?? "";
|
|
49376
49565
|
const rec = records.get(key);
|
|
49377
49566
|
if (!rec) return send(res, 404, JSON.stringify({ error: "unknown or stale message key" }), "application/json");
|
|
@@ -49379,13 +49568,13 @@ data: ${JSON.stringify(data)}
|
|
|
49379
49568
|
const body = { key, html: renderMessageHtml(source) };
|
|
49380
49569
|
return send(res, 200, JSON.stringify(body), "application/json");
|
|
49381
49570
|
}
|
|
49382
|
-
if (method === "POST" && (
|
|
49571
|
+
if (method === "POST" && (path17 === "/api/draft" || path17 === "/api/draft/discard")) {
|
|
49383
49572
|
if (!mutationGuards(req)) return send(res, 403, JSON.stringify({ error: "forbidden" }), "application/json");
|
|
49384
49573
|
if (!drafts) return send(res, 503, JSON.stringify({ error: "drafts are not available on this runtime", code: "drafts_unavailable" }), "application/json");
|
|
49385
49574
|
const body = await readJson(req).catch(() => null);
|
|
49386
49575
|
const key = body && typeof body.key === "string" ? body.key : "";
|
|
49387
49576
|
if (!records.has(key)) return send(res, 409, JSON.stringify({ error: "unknown or stale message key" }), "application/json");
|
|
49388
|
-
if (
|
|
49577
|
+
if (path17 === "/api/draft/discard") {
|
|
49389
49578
|
const composeId2 = body && typeof body.composeId === "string" ? body.composeId : "";
|
|
49390
49579
|
const rev = body && Number.isInteger(body.rev) ? Number(body.rev) : NaN;
|
|
49391
49580
|
if (!composeId2 || !Number.isFinite(rev)) return send(res, 400, JSON.stringify({ error: "composeId and rev are required" }), "application/json");
|
|
@@ -49400,7 +49589,7 @@ data: ${JSON.stringify(data)}
|
|
|
49400
49589
|
if (!r.ok) return send(res, 409, JSON.stringify({ error: "draft_conflict", draft: r.conflict }), "application/json");
|
|
49401
49590
|
return send(res, 200, JSON.stringify({ ok: true, draft: r.draft }), "application/json");
|
|
49402
49591
|
}
|
|
49403
|
-
if (method === "POST" && (
|
|
49592
|
+
if (method === "POST" && (path17 === "/api/reply" || path17 === "/api/handle" || path17 === "/api/mark-read")) {
|
|
49404
49593
|
if (!mutationGuards(req)) return send(res, 403, JSON.stringify({ error: "forbidden" }), "application/json");
|
|
49405
49594
|
const body = await readJson(req).catch(() => null);
|
|
49406
49595
|
const key = body && typeof body.key === "string" ? body.key : "";
|
|
@@ -49412,7 +49601,7 @@ data: ${JSON.stringify(data)}
|
|
|
49412
49601
|
const status = code3 === "private_unsupported" || code3 === "unknown_method" ? 422 : code3 === "unknown_agent" || code3 === "ambiguous_agent" || code3 === "offline" ? 409 : 502;
|
|
49413
49602
|
return send(res, status, JSON.stringify({ error: e?.message ?? "control error", code: code3 }), "application/json");
|
|
49414
49603
|
};
|
|
49415
|
-
if (
|
|
49604
|
+
if (path17 === "/api/reply") {
|
|
49416
49605
|
const text5 = body && typeof body.text === "string" ? body.text : "";
|
|
49417
49606
|
if (!text5.trim()) return send(res, 400, JSON.stringify({ error: "empty reply" }), "application/json");
|
|
49418
49607
|
const attemptId = body && typeof body.attemptId === "string" && /^[A-Za-z0-9-]{1,64}$/.test(body.attemptId) ? body.attemptId : void 0;
|
|
@@ -49443,7 +49632,7 @@ data: ${JSON.stringify(data)}
|
|
|
49443
49632
|
emit("sent", { key: rec.key, ...attemptId ? { attemptId } : {}, outcome, ...draftRef ? { draft: draftRef } : {} });
|
|
49444
49633
|
return send(res, 200, JSON.stringify({ ok: true, ...attemptId ? { attemptId } : {}, outcome }), "application/json");
|
|
49445
49634
|
}
|
|
49446
|
-
if (
|
|
49635
|
+
if (path17 === "/api/handle") {
|
|
49447
49636
|
if (handleStarting.has(rec.key)) return send(res, 409, JSON.stringify({ error: "a handoff is already being started for this message", code: "busy" }), "application/json");
|
|
49448
49637
|
handleStarting.add(rec.key);
|
|
49449
49638
|
try {
|
|
@@ -49477,7 +49666,7 @@ data: ${JSON.stringify(data)}
|
|
|
49477
49666
|
emit("updated", publishRow(rec));
|
|
49478
49667
|
return send(res, 200, JSON.stringify({ ok: true }), "application/json");
|
|
49479
49668
|
}
|
|
49480
|
-
if (method === "GET" &&
|
|
49669
|
+
if (method === "GET" && path17 === "/api/events") {
|
|
49481
49670
|
await ensureSubscribed();
|
|
49482
49671
|
res.writeHead(200, { ...securityHeaders(), "Content-Type": "text/event-stream", Connection: "keep-alive" });
|
|
49483
49672
|
res.write(": ok\n\n");
|
|
@@ -49562,7 +49751,7 @@ var init_popup_broker = __esm({
|
|
|
49562
49751
|
// src/popup-drafts.ts
|
|
49563
49752
|
import fs11 from "node:fs";
|
|
49564
49753
|
import np12 from "node:path";
|
|
49565
|
-
import { randomUUID as
|
|
49754
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
49566
49755
|
function openPopupDrafts(opts) {
|
|
49567
49756
|
const now = opts.now ?? (() => Date.now());
|
|
49568
49757
|
const avail = sqliteAvailability();
|
|
@@ -49604,7 +49793,7 @@ function openPopupDrafts(opts) {
|
|
|
49604
49793
|
if (o.composeId === void 0) {
|
|
49605
49794
|
const cur = get(key);
|
|
49606
49795
|
if (cur) return { ok: false, conflict: cur };
|
|
49607
|
-
const composeId =
|
|
49796
|
+
const composeId = randomUUID5();
|
|
49608
49797
|
db.prepare("INSERT INTO popup_drafts (key, compose_id, rev, text, at) VALUES (?, ?, 1, ?, ?)").run(key, composeId, text5, at);
|
|
49609
49798
|
return { ok: true, draft: { composeId, rev: 1, text: text5, at } };
|
|
49610
49799
|
}
|
|
@@ -49613,7 +49802,7 @@ function openPopupDrafts(opts) {
|
|
|
49613
49802
|
if (Number(r.changes) !== 1) {
|
|
49614
49803
|
const cur = get(key);
|
|
49615
49804
|
if (!cur) {
|
|
49616
|
-
const composeId =
|
|
49805
|
+
const composeId = randomUUID5();
|
|
49617
49806
|
db.prepare("INSERT INTO popup_drafts (key, compose_id, rev, text, at) VALUES (?, ?, 1, ?, ?)").run(key, composeId, text5, at);
|
|
49618
49807
|
return { ok: true, draft: { composeId, rev: 1, text: text5, at } };
|
|
49619
49808
|
}
|
|
@@ -49658,12 +49847,12 @@ function memoryDrafts(now) {
|
|
|
49658
49847
|
const cur = m.get(key) ?? null;
|
|
49659
49848
|
if (o.composeId === void 0) {
|
|
49660
49849
|
if (cur) return { ok: false, conflict: cur };
|
|
49661
|
-
const d2 = { composeId:
|
|
49850
|
+
const d2 = { composeId: randomUUID5(), rev: 1, text: text5, at };
|
|
49662
49851
|
m.set(key, d2);
|
|
49663
49852
|
return { ok: true, draft: d2 };
|
|
49664
49853
|
}
|
|
49665
49854
|
if (!cur) {
|
|
49666
|
-
const d2 = { composeId:
|
|
49855
|
+
const d2 = { composeId: randomUUID5(), rev: 1, text: text5, at };
|
|
49667
49856
|
m.set(key, d2);
|
|
49668
49857
|
return { ok: true, draft: d2 };
|
|
49669
49858
|
}
|
|
@@ -49803,7 +49992,7 @@ function serverHostLabel2(server2, allServers) {
|
|
|
49803
49992
|
} catch {
|
|
49804
49993
|
return safeHandle(String(server2));
|
|
49805
49994
|
}
|
|
49806
|
-
const
|
|
49995
|
+
const path17 = u.pathname.replace(/\/+$/, "");
|
|
49807
49996
|
let label = u.host;
|
|
49808
49997
|
if (allServers) {
|
|
49809
49998
|
const mine = normalizeServerUrl(String(server2));
|
|
@@ -49817,8 +50006,8 @@ function serverHostLabel2(server2, allServers) {
|
|
|
49817
50006
|
}
|
|
49818
50007
|
const clash = (key) => others.some((o) => key(o) === key(u));
|
|
49819
50008
|
if (clash((x) => x.host)) {
|
|
49820
|
-
label = u.host +
|
|
49821
|
-
if (clash((x) => x.host + x.pathname.replace(/\/+$/, ""))) label = `${u.protocol}//${u.host}${
|
|
50009
|
+
label = u.host + path17;
|
|
50010
|
+
if (clash((x) => x.host + x.pathname.replace(/\/+$/, ""))) label = `${u.protocol}//${u.host}${path17}`;
|
|
49822
50011
|
}
|
|
49823
50012
|
}
|
|
49824
50013
|
return safeHandle(label);
|
|
@@ -51788,61 +51977,6 @@ var init_version = __esm({
|
|
|
51788
51977
|
}
|
|
51789
51978
|
});
|
|
51790
51979
|
|
|
51791
|
-
// src/send-confirmed.ts
|
|
51792
|
-
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
51793
|
-
function sendConfirmed(client, target, text5, opts = {}) {
|
|
51794
|
-
const clientId = opts.clientId ?? randomUUID5();
|
|
51795
|
-
const timeoutMs = opts.timeoutMs ?? SEND_CONFIRM_TIMEOUT_MS;
|
|
51796
|
-
return new Promise((resolve) => {
|
|
51797
|
-
let settled = false;
|
|
51798
|
-
const finish = (out) => {
|
|
51799
|
-
if (settled) return;
|
|
51800
|
-
settled = true;
|
|
51801
|
-
clearTimeout(timer);
|
|
51802
|
-
clearInterval(linkCheck);
|
|
51803
|
-
client.off("reply_ack", onAck);
|
|
51804
|
-
client.off("error", onErr);
|
|
51805
|
-
resolve(out);
|
|
51806
|
-
};
|
|
51807
|
-
const onAck = (ev) => {
|
|
51808
|
-
if (ev?.replyId !== clientId) return;
|
|
51809
|
-
finish({ outcome: "sent", hubMessageId: String(ev.messageId ?? "") });
|
|
51810
|
-
};
|
|
51811
|
-
const onErr = (ev) => {
|
|
51812
|
-
if (ev?.replyId !== clientId) return;
|
|
51813
|
-
finish({
|
|
51814
|
-
outcome: "rejected",
|
|
51815
|
-
reason: String(ev.message ?? "the hub refused the message"),
|
|
51816
|
-
...typeof ev.code === "string" ? { code: ev.code } : {},
|
|
51817
|
-
...typeof ev.status === "number" ? { status: ev.status } : {}
|
|
51818
|
-
});
|
|
51819
|
-
};
|
|
51820
|
-
const timer = setTimeout(
|
|
51821
|
-
() => finish({ outcome: "ambiguous", reason: `no answer from the hub within ${Math.round(timeoutMs / 1e3)}s \u2014 check history before retrying` }),
|
|
51822
|
-
timeoutMs
|
|
51823
|
-
);
|
|
51824
|
-
const linkCheck = setInterval(() => {
|
|
51825
|
-
if (client.online === false) finish({ outcome: "ambiguous", reason: "the hub connection dropped before it answered \u2014 check history before retrying" });
|
|
51826
|
-
}, 250);
|
|
51827
|
-
client.on("reply_ack", onAck);
|
|
51828
|
-
client.on("error", onErr);
|
|
51829
|
-
try {
|
|
51830
|
-
if (target.groupId) client.groupMessage(target.groupId, text5, { clientId });
|
|
51831
|
-
else if (target.to) client.message(target.to, text5, { clientId });
|
|
51832
|
-
else return finish({ outcome: "rejected", reason: "need `to` or `groupId`" });
|
|
51833
|
-
} catch (e) {
|
|
51834
|
-
finish({ outcome: "rejected", reason: e?.message ?? "send failed" });
|
|
51835
|
-
}
|
|
51836
|
-
});
|
|
51837
|
-
}
|
|
51838
|
-
var SEND_CONFIRM_TIMEOUT_MS;
|
|
51839
|
-
var init_send_confirmed = __esm({
|
|
51840
|
-
"src/send-confirmed.ts"() {
|
|
51841
|
-
"use strict";
|
|
51842
|
-
SEND_CONFIRM_TIMEOUT_MS = 8e3;
|
|
51843
|
-
}
|
|
51844
|
-
});
|
|
51845
|
-
|
|
51846
51980
|
// src/identity-lock.ts
|
|
51847
51981
|
import fs17 from "node:fs";
|
|
51848
51982
|
import os8 from "node:os";
|
|
@@ -55163,6 +55297,158 @@ var init_secret_input = __esm({
|
|
|
55163
55297
|
}
|
|
55164
55298
|
});
|
|
55165
55299
|
|
|
55300
|
+
// plugin/claude-layout.mjs
|
|
55301
|
+
import fs27 from "node:fs";
|
|
55302
|
+
import path13 from "node:path";
|
|
55303
|
+
function buildPluginManifest(version2) {
|
|
55304
|
+
return {
|
|
55305
|
+
name: "jefri-chat",
|
|
55306
|
+
displayName: "Jefri Chat",
|
|
55307
|
+
version: version2,
|
|
55308
|
+
description: "WhatsApp for AI agents \u2014 local Claude Desktop connector. Needs Node installed. Configure this desktop identity with plugin install --app claude before importing; credentials stay on this machine, outside the plugin.",
|
|
55309
|
+
author: { name: "Jefri Chat", url: "https://jefrichat.com" },
|
|
55310
|
+
homepage: "https://jefrichat.com",
|
|
55311
|
+
license: "UNLICENSED",
|
|
55312
|
+
keywords: ["mcp", "agents", "chat", "jefri"]
|
|
55313
|
+
};
|
|
55314
|
+
}
|
|
55315
|
+
function buildPluginMcp() {
|
|
55316
|
+
return {
|
|
55317
|
+
mcpServers: {
|
|
55318
|
+
jefri: {
|
|
55319
|
+
command: "node",
|
|
55320
|
+
args: ["${CLAUDE_PLUGIN_ROOT}/server/launch.mjs"],
|
|
55321
|
+
env: { JEFRI_CONNECTOR_PROFILE: "claude_desktop", JEFRI_CLAUDE_DESKTOP_RECOVERY: "1" }
|
|
55322
|
+
}
|
|
55323
|
+
}
|
|
55324
|
+
};
|
|
55325
|
+
}
|
|
55326
|
+
function writePluginFiles(out, version2) {
|
|
55327
|
+
fs27.mkdirSync(path13.join(out, ".claude-plugin"), { recursive: true });
|
|
55328
|
+
fs27.mkdirSync(path13.join(out, "server"), { recursive: true });
|
|
55329
|
+
fs27.writeFileSync(path13.join(out, ".claude-plugin", "plugin.json"), JSON.stringify(buildPluginManifest(version2), null, 2) + "\n");
|
|
55330
|
+
fs27.writeFileSync(path13.join(out, ".mcp.json"), JSON.stringify(buildPluginMcp(), null, 2) + "\n");
|
|
55331
|
+
fs27.writeFileSync(path13.join(out, "package.json"), JSON.stringify({ name: "jefri-chat-desktop-plugin", version: version2, private: true, type: "module" }, null, 2) + "\n");
|
|
55332
|
+
fs27.writeFileSync(path13.join(out, "server", "launch.mjs"), CLAUDE_LAUNCHER);
|
|
55333
|
+
}
|
|
55334
|
+
var CLAUDE_LAUNCHER;
|
|
55335
|
+
var init_claude_layout = __esm({
|
|
55336
|
+
"plugin/claude-layout.mjs"() {
|
|
55337
|
+
"use strict";
|
|
55338
|
+
CLAUDE_LAUNCHER = `import fs from "node:fs";
|
|
55339
|
+
import os from "node:os";
|
|
55340
|
+
import path from "node:path";
|
|
55341
|
+
const configPath = path.join(os.homedir(), ".jefri", "claude-desktop", "connection.json");
|
|
55342
|
+
let fd;
|
|
55343
|
+
try {
|
|
55344
|
+
fd = fs.openSync(configPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
55345
|
+
const st = fs.fstatSync(fd);
|
|
55346
|
+
if (!st.isFile() || st.size > 16384 || (process.platform !== "win32" &&
|
|
55347
|
+
((st.mode & 0o077) !== 0 || (process.getuid && st.uid !== process.getuid())))) throw new Error();
|
|
55348
|
+
const c = JSON.parse(fs.readFileSync(fd, "utf8"));
|
|
55349
|
+
const hub = new URL(c.server);
|
|
55350
|
+
if (c.schema !== 1 || typeof c.agent !== "string" || !c.agent.trim() ||
|
|
55351
|
+
typeof c.token !== "string" || !c.token.trim() || /[\\r\\n]/.test(c.token) ||
|
|
55352
|
+
!["http:", "https:"].includes(hub.protocol) || hub.username || hub.password ||
|
|
55353
|
+
hub.search || hub.hash || hub.pathname !== "/") throw new Error();
|
|
55354
|
+
for (const k of ["JEFRI_AS", "JEFRI_PROFILE", "JEFRI_LIVE_HANDOFF", "JEFRI_DAEMON", "JEFRI_TAKEOVER_TICKET", "JEFRI_OWNER_TOKEN"]) delete process.env[k];
|
|
55355
|
+
process.env.JEFRI_TOKEN = c.token;
|
|
55356
|
+
process.env.JEFRI_SERVER = hub.origin;
|
|
55357
|
+
process.env.JEFRI_CONNECTOR_PROFILE = "claude_desktop";
|
|
55358
|
+
process.env.JEFRI_CLAUDE_DESKTOP_RECOVERY = "1";
|
|
55359
|
+
} catch {
|
|
55360
|
+
console.error("Jefri Claude Desktop setup is missing or invalid. Run this connector's plugin install --app claude --agent <username> command; enter the token at its hidden prompt. The private connection.json must be owned by you and mode 0600. No other identity was used.");
|
|
55361
|
+
process.exitCode = 1;
|
|
55362
|
+
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
55363
|
+
if (!process.exitCode) await import("./index.mjs");
|
|
55364
|
+
`;
|
|
55365
|
+
}
|
|
55366
|
+
});
|
|
55367
|
+
|
|
55368
|
+
// src/claude-plugin-setup.ts
|
|
55369
|
+
import fs28 from "node:fs";
|
|
55370
|
+
import path14 from "node:path";
|
|
55371
|
+
import { execFileSync } from "node:child_process";
|
|
55372
|
+
function installClaudePlugin(opts) {
|
|
55373
|
+
const hub = normalizeHub2(opts.server);
|
|
55374
|
+
if (!opts.agent || opts.agent.length > 128 || /\s/.test(opts.agent)) throw new Error("agent username is required");
|
|
55375
|
+
const version2 = bundleVersionOf(opts.bundlePath);
|
|
55376
|
+
if (!version2 || !fs28.statSync(opts.bundlePath).isFile()) throw new Error("cannot determine the pinned connector bundle and version; nothing was written");
|
|
55377
|
+
const dir = claudePluginDir(opts.home);
|
|
55378
|
+
const lock2 = acquireInstallLock(path14.join(opts.home, ".jefri", "claude-desktop-install.lock"));
|
|
55379
|
+
let stage;
|
|
55380
|
+
let previous3;
|
|
55381
|
+
let swapped = false;
|
|
55382
|
+
try {
|
|
55383
|
+
let old;
|
|
55384
|
+
if (fs28.existsSync(dir)) {
|
|
55385
|
+
if (fs28.lstatSync(dir).isSymbolicLink() || !fs28.existsSync(path14.join(dir, MARKER)))
|
|
55386
|
+
throw new Error("Claude Desktop setup directory is not managed by this installer; left untouched");
|
|
55387
|
+
try {
|
|
55388
|
+
const file = path14.join(dir, "connection.json");
|
|
55389
|
+
const st = fs28.lstatSync(file);
|
|
55390
|
+
if (!st.isFile() || st.size > 16384 || process.platform !== "win32" && ((st.mode & 63) !== 0 || st.uid !== process.getuid?.())) throw new Error();
|
|
55391
|
+
old = JSON.parse(fs28.readFileSync(file, "utf8"));
|
|
55392
|
+
} catch {
|
|
55393
|
+
throw new Error("existing Claude Desktop configuration is unreadable or not private; left untouched");
|
|
55394
|
+
}
|
|
55395
|
+
}
|
|
55396
|
+
const token = opts.token || (old?.agent === opts.agent && old?.server === hub ? old.token : void 0);
|
|
55397
|
+
if (typeof token !== "string" || token.length > 4096 || !token.trim() || /[\r\n]/.test(token))
|
|
55398
|
+
throw new Error("no token: enter this Claude Desktop agent's token at the hidden prompt; nothing was written");
|
|
55399
|
+
execFileSync("zip", ["-v"], { stdio: "ignore" });
|
|
55400
|
+
stage = fs28.mkdtempSync(path14.join(path14.dirname(dir), ".claude-desktop-stage-"));
|
|
55401
|
+
fs28.chmodSync(stage, 448);
|
|
55402
|
+
fs28.writeFileSync(path14.join(stage, MARKER), "1\n", { mode: 384 });
|
|
55403
|
+
fs28.writeFileSync(path14.join(stage, "connection.json"), JSON.stringify({ schema: 1, agent: opts.agent, server: hub, token }, null, 2) + "\n", { mode: 384 });
|
|
55404
|
+
const plugin = path14.join(stage, "plugin");
|
|
55405
|
+
writePluginFiles(plugin, version2);
|
|
55406
|
+
fs28.copyFileSync(opts.bundlePath, path14.join(plugin, "server", "index.mjs"));
|
|
55407
|
+
execFileSync("zip", ["-q", "-r", "-X", path14.join(stage, "jefri-chat.plugin"), ".claude-plugin", ".mcp.json", "package.json", "server"], { cwd: plugin, stdio: "ignore" });
|
|
55408
|
+
lock2.assertHeld("replacing Claude Desktop setup");
|
|
55409
|
+
if (fs28.existsSync(dir)) {
|
|
55410
|
+
previous3 = `${stage}.previous`;
|
|
55411
|
+
fs28.renameSync(dir, previous3);
|
|
55412
|
+
}
|
|
55413
|
+
try {
|
|
55414
|
+
fs28.renameSync(stage, dir);
|
|
55415
|
+
swapped = true;
|
|
55416
|
+
} catch (e) {
|
|
55417
|
+
if (previous3) {
|
|
55418
|
+
try {
|
|
55419
|
+
fs28.renameSync(previous3, dir);
|
|
55420
|
+
previous3 = void 0;
|
|
55421
|
+
} catch {
|
|
55422
|
+
throw new Error(`could not activate or restore Claude Desktop setup; previous setup is intact at ${previous3}`);
|
|
55423
|
+
}
|
|
55424
|
+
}
|
|
55425
|
+
throw e;
|
|
55426
|
+
}
|
|
55427
|
+
return {
|
|
55428
|
+
archive: path14.join(dir, "jefri-chat.plugin"),
|
|
55429
|
+
configPath: path14.join(dir, "connection.json"),
|
|
55430
|
+
version: version2,
|
|
55431
|
+
hub,
|
|
55432
|
+
agent: opts.agent,
|
|
55433
|
+
identity: opts.token ? "stored" : "kept",
|
|
55434
|
+
previous: previous3
|
|
55435
|
+
};
|
|
55436
|
+
} finally {
|
|
55437
|
+
if (stage && !swapped) fs28.rmSync(stage, { recursive: true, force: true });
|
|
55438
|
+
lock2.release();
|
|
55439
|
+
}
|
|
55440
|
+
}
|
|
55441
|
+
var claudePluginDir, MARKER;
|
|
55442
|
+
var init_claude_plugin_setup = __esm({
|
|
55443
|
+
"src/claude-plugin-setup.ts"() {
|
|
55444
|
+
"use strict";
|
|
55445
|
+
init_claude_layout();
|
|
55446
|
+
init_plugin_setup();
|
|
55447
|
+
claudePluginDir = (home) => path14.join(home, ".jefri", "claude-desktop");
|
|
55448
|
+
MARKER = ".jefri-claude-install";
|
|
55449
|
+
}
|
|
55450
|
+
});
|
|
55451
|
+
|
|
55166
55452
|
// src/plugin-cli.ts
|
|
55167
55453
|
var plugin_cli_exports = {};
|
|
55168
55454
|
__export(plugin_cli_exports, {
|
|
@@ -55170,7 +55456,7 @@ __export(plugin_cli_exports, {
|
|
|
55170
55456
|
runPluginCli: () => runPluginCli,
|
|
55171
55457
|
selfBundlePath: () => selfBundlePath
|
|
55172
55458
|
});
|
|
55173
|
-
import
|
|
55459
|
+
import fs29 from "node:fs";
|
|
55174
55460
|
import os15 from "node:os";
|
|
55175
55461
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
55176
55462
|
function selfBundlePath() {
|
|
@@ -55184,12 +55470,12 @@ async function runPluginCli(argv, deps = {}) {
|
|
|
55184
55470
|
const err = deps.err ?? ((l) => console.error(l));
|
|
55185
55471
|
const sub = argv[0];
|
|
55186
55472
|
if (sub !== "install") {
|
|
55187
|
-
err("usage: jefrichat-mcp plugin install --app chatgpt|codex --agent <username> [--server <hub url>] (the token is asked for hidden, or read from JEFRI_TOKEN)");
|
|
55473
|
+
err("usage: jefrichat-mcp plugin install --app chatgpt|codex|claude --agent <username> [--server <hub url>] (the token is asked for hidden, or read from JEFRI_TOKEN)");
|
|
55188
55474
|
return 2;
|
|
55189
55475
|
}
|
|
55190
55476
|
const app = argValue(argv, "--app") ?? "chatgpt";
|
|
55191
|
-
if (app !== "chatgpt" && app !== "codex") {
|
|
55192
|
-
err(`--app must be chatgpt or
|
|
55477
|
+
if (app !== "chatgpt" && app !== "codex" && app !== "claude") {
|
|
55478
|
+
err(`--app must be chatgpt, codex or claude, got ${JSON.stringify(app)}`);
|
|
55193
55479
|
return 2;
|
|
55194
55480
|
}
|
|
55195
55481
|
const agent = argValue(argv, "--agent");
|
|
@@ -55201,12 +55487,13 @@ async function runPluginCli(argv, deps = {}) {
|
|
|
55201
55487
|
err("refusing --token on the command line: it never belongs in shell history or a process list \u2014 run without it and paste the token at the hidden prompt (or set JEFRI_TOKEN for scripts)");
|
|
55202
55488
|
return 2;
|
|
55203
55489
|
}
|
|
55204
|
-
|
|
55205
|
-
|
|
55490
|
+
const connectorProfile = app === "claude" ? "claude_desktop" : PLUGIN_CONNECTOR_PROFILE;
|
|
55491
|
+
if (env.JEFRI_CONNECTOR_PROFILE && env.JEFRI_CONNECTOR_PROFILE !== connectorProfile)
|
|
55492
|
+
err(`ignoring JEFRI_CONNECTOR_PROFILE=${JSON.stringify(env.JEFRI_CONNECTOR_PROFILE)}: this desktop plugin uses ${connectorProfile} (the agent identity is separate)`);
|
|
55206
55493
|
const server2 = argValue(argv, "--server") || env.JEFRI_SERVER || "https://jefrichat.com";
|
|
55207
55494
|
const home = deps.home ?? os15.homedir();
|
|
55208
55495
|
const bundlePath = deps.bundlePath ?? selfBundlePath();
|
|
55209
|
-
if (!
|
|
55496
|
+
if (!fs29.existsSync(bundlePath)) {
|
|
55210
55497
|
err(`cannot find the connector bundle to pin (${bundlePath})`);
|
|
55211
55498
|
return 1;
|
|
55212
55499
|
}
|
|
@@ -55221,6 +55508,22 @@ async function runPluginCli(argv, deps = {}) {
|
|
|
55221
55508
|
}
|
|
55222
55509
|
}
|
|
55223
55510
|
let r;
|
|
55511
|
+
if (app === "claude") {
|
|
55512
|
+
try {
|
|
55513
|
+
const c2 = installClaudePlugin({ agent, server: server2, token, home, bundlePath });
|
|
55514
|
+
out(`Claude Desktop package prepared (connector ${c2.version}): ${c2.archive}`);
|
|
55515
|
+
out(`Private desktop identity ${c2.identity}: ${c2.configPath} (0600, not inside the plugin)`);
|
|
55516
|
+
if (c2.previous) out(`Previous setup kept privately for recovery: ${c2.previous}`);
|
|
55517
|
+
out("Not activated or verified yet. Claude Desktop -> Settings -> Plugins -> Upload local plugin: import the file above, replacing the old Jefri plugin, then start a NEW chat.");
|
|
55518
|
+
out("Use only one Jefri plugin/extension for this identity. Do not enable an old extension alongside it. Other apps and shared saved identities were not changed.");
|
|
55519
|
+
out(`Expected identity: @${safeHandle(agent)} on ${c2.hub}. Capability profile: claude_desktop.`);
|
|
55520
|
+
out(VERIFY_PROMPT);
|
|
55521
|
+
return 0;
|
|
55522
|
+
} catch (e) {
|
|
55523
|
+
err(`Claude Desktop setup failed: ${String(e?.message ?? e).replaceAll(token ?? "\0", "[redacted]")}`);
|
|
55524
|
+
return 1;
|
|
55525
|
+
}
|
|
55526
|
+
}
|
|
55224
55527
|
try {
|
|
55225
55528
|
r = installLocalPlugin({ agent, server: server2, token, app, home, bundlePath });
|
|
55226
55529
|
} catch (e) {
|
|
@@ -55251,6 +55554,7 @@ var init_plugin_cli = __esm({
|
|
|
55251
55554
|
init_src();
|
|
55252
55555
|
init_plugin_setup();
|
|
55253
55556
|
init_secret_input();
|
|
55557
|
+
init_claude_plugin_setup();
|
|
55254
55558
|
argValue = (argv, flag) => {
|
|
55255
55559
|
const i = argv.indexOf(flag);
|
|
55256
55560
|
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : void 0;
|
|
@@ -55630,41 +55934,41 @@ var init_daemon_control = __esm({
|
|
|
55630
55934
|
});
|
|
55631
55935
|
|
|
55632
55936
|
// src/daemon.ts
|
|
55633
|
-
import * as
|
|
55937
|
+
import * as fs30 from "node:fs";
|
|
55634
55938
|
import * as np18 from "node:path";
|
|
55635
55939
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
55636
55940
|
import { createHash as createHash9 } from "node:crypto";
|
|
55637
|
-
function realOpenExclusive(
|
|
55638
|
-
const tmp = `${
|
|
55639
|
-
|
|
55941
|
+
function realOpenExclusive(path17, content3) {
|
|
55942
|
+
const tmp = `${path17}.${process.pid}.${randomBytes13(6).toString("hex")}.tmp`;
|
|
55943
|
+
fs30.writeFileSync(tmp, content3, { mode: 384 });
|
|
55640
55944
|
try {
|
|
55641
|
-
|
|
55945
|
+
fs30.linkSync(tmp, path17);
|
|
55642
55946
|
return true;
|
|
55643
55947
|
} catch (e) {
|
|
55644
55948
|
if (e.code === "EEXIST") return false;
|
|
55645
55949
|
throw e;
|
|
55646
55950
|
} finally {
|
|
55647
55951
|
try {
|
|
55648
|
-
|
|
55952
|
+
fs30.unlinkSync(tmp);
|
|
55649
55953
|
} catch {
|
|
55650
55954
|
}
|
|
55651
55955
|
}
|
|
55652
55956
|
}
|
|
55653
|
-
function realReadFile(
|
|
55957
|
+
function realReadFile(path17) {
|
|
55654
55958
|
try {
|
|
55655
|
-
return
|
|
55959
|
+
return fs30.readFileSync(path17, "utf8");
|
|
55656
55960
|
} catch {
|
|
55657
55961
|
return null;
|
|
55658
55962
|
}
|
|
55659
55963
|
}
|
|
55660
|
-
function realWriteFileAtomic(
|
|
55661
|
-
const tmp = `${
|
|
55662
|
-
|
|
55663
|
-
|
|
55964
|
+
function realWriteFileAtomic(path17, content3) {
|
|
55965
|
+
const tmp = `${path17}.${process.pid}.${randomBytes13(6).toString("hex")}.tmp`;
|
|
55966
|
+
fs30.writeFileSync(tmp, content3, { mode: 384 });
|
|
55967
|
+
fs30.renameSync(tmp, path17);
|
|
55664
55968
|
}
|
|
55665
|
-
function realRemoveFile(
|
|
55969
|
+
function realRemoveFile(path17) {
|
|
55666
55970
|
try {
|
|
55667
|
-
|
|
55971
|
+
fs30.unlinkSync(path17);
|
|
55668
55972
|
} catch {
|
|
55669
55973
|
}
|
|
55670
55974
|
}
|
|
@@ -56099,7 +56403,7 @@ var init_daemon_proxy_proto = __esm({
|
|
|
56099
56403
|
});
|
|
56100
56404
|
|
|
56101
56405
|
// src/daemon-server.ts
|
|
56102
|
-
import
|
|
56406
|
+
import fs31 from "node:fs";
|
|
56103
56407
|
import net4 from "node:net";
|
|
56104
56408
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
56105
56409
|
function errToCode(e) {
|
|
@@ -56365,7 +56669,7 @@ async function startDaemonServer(opts) {
|
|
|
56365
56669
|
});
|
|
56366
56670
|
if (process.platform !== "win32") {
|
|
56367
56671
|
try {
|
|
56368
|
-
|
|
56672
|
+
fs31.chmodSync(opts.socketPath, 384);
|
|
56369
56673
|
} catch {
|
|
56370
56674
|
}
|
|
56371
56675
|
}
|
|
@@ -56461,7 +56765,7 @@ async function startDaemonServer(opts) {
|
|
|
56461
56765
|
server2.close(() => {
|
|
56462
56766
|
if (process.platform !== "win32") {
|
|
56463
56767
|
try {
|
|
56464
|
-
|
|
56768
|
+
fs31.unlinkSync(opts.socketPath);
|
|
56465
56769
|
} catch {
|
|
56466
56770
|
}
|
|
56467
56771
|
}
|
|
@@ -56913,13 +57217,13 @@ var init_proxy = __esm({
|
|
|
56913
57217
|
if (typeof t.unref === "function") t.unref();
|
|
56914
57218
|
});
|
|
56915
57219
|
if (this.closed) return;
|
|
56916
|
-
let
|
|
57220
|
+
let path17 = this.opts.socketPath;
|
|
56917
57221
|
try {
|
|
56918
|
-
if (this.opts.resolveSocket)
|
|
57222
|
+
if (this.opts.resolveSocket) path17 = await this.opts.resolveSocket();
|
|
56919
57223
|
} catch {
|
|
56920
57224
|
}
|
|
56921
57225
|
try {
|
|
56922
|
-
const r = await this.startDial(
|
|
57226
|
+
const r = await this.startDial(path17);
|
|
56923
57227
|
try {
|
|
56924
57228
|
this.opts.onReattached?.(r);
|
|
56925
57229
|
} catch {
|
|
@@ -56959,7 +57263,7 @@ var init_proxy = __esm({
|
|
|
56959
57263
|
});
|
|
56960
57264
|
|
|
56961
57265
|
// src/inject-quarantine.ts
|
|
56962
|
-
import
|
|
57266
|
+
import fs32 from "node:fs";
|
|
56963
57267
|
import cp14 from "node:child_process";
|
|
56964
57268
|
import np19 from "node:path";
|
|
56965
57269
|
import { createHash as createHash11, randomBytes as randomBytes16 } from "node:crypto";
|
|
@@ -57025,7 +57329,7 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57025
57329
|
const read = () => {
|
|
57026
57330
|
let raw;
|
|
57027
57331
|
try {
|
|
57028
|
-
raw =
|
|
57332
|
+
raw = fs32.readFileSync(file, "utf8");
|
|
57029
57333
|
} catch {
|
|
57030
57334
|
return "absent";
|
|
57031
57335
|
}
|
|
@@ -57044,7 +57348,7 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57044
57348
|
const pruneDeadGenerations = () => {
|
|
57045
57349
|
let names;
|
|
57046
57350
|
try {
|
|
57047
|
-
names =
|
|
57351
|
+
names = fs32.readdirSync(dir);
|
|
57048
57352
|
} catch {
|
|
57049
57353
|
return;
|
|
57050
57354
|
}
|
|
@@ -57054,14 +57358,14 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57054
57358
|
const p = np19.join(dir, n);
|
|
57055
57359
|
let body;
|
|
57056
57360
|
try {
|
|
57057
|
-
body = JSON.parse(
|
|
57361
|
+
body = JSON.parse(fs32.readFileSync(p, "utf8"));
|
|
57058
57362
|
} catch {
|
|
57059
57363
|
continue;
|
|
57060
57364
|
}
|
|
57061
57365
|
if (typeof body.panePid !== "number") continue;
|
|
57062
57366
|
if (!paneAlive(body.panePid, body.paneToken ?? "")) {
|
|
57063
57367
|
try {
|
|
57064
|
-
|
|
57368
|
+
fs32.unlinkSync(p);
|
|
57065
57369
|
} catch {
|
|
57066
57370
|
}
|
|
57067
57371
|
}
|
|
@@ -57080,22 +57384,22 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57080
57384
|
};
|
|
57081
57385
|
const tmp = `${file}.${writer.pid}.${attempt}.tmp`;
|
|
57082
57386
|
try {
|
|
57083
|
-
|
|
57084
|
-
|
|
57387
|
+
fs32.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
57388
|
+
fs32.writeFileSync(tmp, JSON.stringify(body), { mode: 384 });
|
|
57085
57389
|
} catch (e) {
|
|
57086
57390
|
try {
|
|
57087
|
-
|
|
57391
|
+
fs32.unlinkSync(tmp);
|
|
57088
57392
|
} catch {
|
|
57089
57393
|
}
|
|
57090
57394
|
return { ok: false, reason: `could not record the write-ahead marker (${e.code ?? e}) \u2014 refusing to touch the terminal` };
|
|
57091
57395
|
}
|
|
57092
57396
|
try {
|
|
57093
|
-
|
|
57094
|
-
|
|
57397
|
+
fs32.linkSync(tmp, file);
|
|
57398
|
+
fs32.unlinkSync(tmp);
|
|
57095
57399
|
return { ok: true, attempt };
|
|
57096
57400
|
} catch (e) {
|
|
57097
57401
|
try {
|
|
57098
|
-
|
|
57402
|
+
fs32.unlinkSync(tmp);
|
|
57099
57403
|
} catch {
|
|
57100
57404
|
}
|
|
57101
57405
|
const code3 = e.code;
|
|
@@ -57141,7 +57445,7 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57141
57445
|
if (body.attempt !== attempt || body.writerPid !== writer.pid) return;
|
|
57142
57446
|
if (outcome === "clean") {
|
|
57143
57447
|
try {
|
|
57144
|
-
|
|
57448
|
+
fs32.unlinkSync(file);
|
|
57145
57449
|
} catch {
|
|
57146
57450
|
}
|
|
57147
57451
|
return;
|
|
@@ -57149,8 +57453,8 @@ function createInjectQuarantine(resolution2, dir = dirFor(), deps = {}) {
|
|
|
57149
57453
|
const dirty = { ...body, state: "dirty", reason: outcome.dirty, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
57150
57454
|
try {
|
|
57151
57455
|
const tmp = `${file}.${writer.pid}.dirty.tmp`;
|
|
57152
|
-
|
|
57153
|
-
|
|
57456
|
+
fs32.writeFileSync(tmp, JSON.stringify(dirty), { mode: 384 });
|
|
57457
|
+
fs32.renameSync(tmp, file);
|
|
57154
57458
|
} catch {
|
|
57155
57459
|
}
|
|
57156
57460
|
}
|
|
@@ -57185,16 +57489,16 @@ __export(daemon_mode_exports, {
|
|
|
57185
57489
|
wireInboxEvents: () => wireInboxEvents,
|
|
57186
57490
|
wireIncomingDelivery: () => wireIncomingDelivery
|
|
57187
57491
|
});
|
|
57188
|
-
import
|
|
57492
|
+
import fs33 from "node:fs";
|
|
57189
57493
|
import os17 from "node:os";
|
|
57190
|
-
import
|
|
57494
|
+
import path15 from "node:path";
|
|
57191
57495
|
function resolveIdentityEnv() {
|
|
57192
57496
|
const agent = process.env.JEFRI_AS;
|
|
57193
57497
|
const token = process.env.JEFRI_TOKEN;
|
|
57194
57498
|
if (agent && token) return { agent, token };
|
|
57195
57499
|
if (agent) {
|
|
57196
57500
|
try {
|
|
57197
|
-
const cache = JSON.parse(
|
|
57501
|
+
const cache = JSON.parse(fs33.readFileSync(jefriPath("tokens.json"), "utf8"));
|
|
57198
57502
|
const server2 = process.env.JEFRI_SERVER ?? "http://localhost:4000";
|
|
57199
57503
|
const cached2 = cache[`${server2}::${agent}`];
|
|
57200
57504
|
if (cached2) return { agent, token: cached2 };
|
|
@@ -57910,7 +58214,7 @@ async function runDaemonMode() {
|
|
|
57910
58214
|
}
|
|
57911
58215
|
const transport = new StdioServerTransport();
|
|
57912
58216
|
await server2.connect(transport);
|
|
57913
|
-
log(`proxy ready \u2014 @${safeHandle(id.agent)} via ${
|
|
58217
|
+
log(`proxy ready \u2014 @${safeHandle(id.agent)} via ${path15.basename(socketPath)}${daemonStopRef.stop ? " (this process IS the daemon)" : ""}`);
|
|
57914
58218
|
let shuttingDown = false;
|
|
57915
58219
|
const shutdown = () => {
|
|
57916
58220
|
if (shuttingDown) return;
|
|
@@ -58036,12 +58340,19 @@ init_state_root();
|
|
|
58036
58340
|
init_src();
|
|
58037
58341
|
init_stdio2();
|
|
58038
58342
|
init_src2();
|
|
58039
|
-
|
|
58040
|
-
import fs32 from "node:fs";
|
|
58343
|
+
import fs34 from "node:fs";
|
|
58041
58344
|
import os18 from "node:os";
|
|
58042
|
-
import
|
|
58345
|
+
import path16 from "node:path";
|
|
58043
58346
|
import { randomBytes as randomBytes17 } from "node:crypto";
|
|
58044
58347
|
|
|
58348
|
+
// src/claude-desktop-recovery.ts
|
|
58349
|
+
function claudeDesktopRecoveryEnabled(env = process.env) {
|
|
58350
|
+
return env.JEFRI_CONNECTOR_PROFILE === "claude_desktop" && env.JEFRI_CLAUDE_DESKTOP_RECOVERY === "1";
|
|
58351
|
+
}
|
|
58352
|
+
|
|
58353
|
+
// src/index.ts
|
|
58354
|
+
init_src();
|
|
58355
|
+
|
|
58045
58356
|
// src/group-cache.ts
|
|
58046
58357
|
function createGroupCache(opts) {
|
|
58047
58358
|
const gateTimeoutMs = opts.gateTimeoutMs ?? 1e4;
|
|
@@ -59367,20 +59678,21 @@ var USERNAME = process.env.JEFRI_AS ?? "claude_agent";
|
|
|
59367
59678
|
var NAME = process.env.JEFRI_NAME ?? USERNAME;
|
|
59368
59679
|
var TAGS = (process.env.JEFRI_TAGS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
59369
59680
|
var CAPS = (process.env.JEFRI_CAPABILITIES ?? "coding").split(",").map((s) => s.trim()).filter(Boolean);
|
|
59681
|
+
var DESKTOP_RECOVERY = claudeDesktopRecoveryEnabled();
|
|
59370
59682
|
var log2 = (...a) => console.error("[jefrichat-mcp]", ...a);
|
|
59371
59683
|
var VERBOSE = process.env.JEFRI_DEBUG === "1" || process.env.JEFRI_VERBOSE === "1";
|
|
59372
59684
|
var note = (...a) => {
|
|
59373
59685
|
if (VERBOSE) log2(...a);
|
|
59374
59686
|
};
|
|
59375
59687
|
var TOKEN_CACHE = () => jefriPath("tokens.json");
|
|
59376
|
-
var LEGACY_TOKEN_CACHE =
|
|
59688
|
+
var LEGACY_TOKEN_CACHE = path16.join(os18.homedir(), ".acp", "tokens.json");
|
|
59377
59689
|
var cacheKey = `${SERVER}::${USERNAME}`;
|
|
59378
59690
|
function readTokenCache() {
|
|
59379
59691
|
try {
|
|
59380
|
-
return JSON.parse(
|
|
59692
|
+
return JSON.parse(fs34.readFileSync(TOKEN_CACHE(), "utf8"));
|
|
59381
59693
|
} catch {
|
|
59382
59694
|
try {
|
|
59383
|
-
return JSON.parse(
|
|
59695
|
+
return JSON.parse(fs34.readFileSync(LEGACY_TOKEN_CACHE, "utf8"));
|
|
59384
59696
|
} catch {
|
|
59385
59697
|
return {};
|
|
59386
59698
|
}
|
|
@@ -59389,8 +59701,8 @@ function readTokenCache() {
|
|
|
59389
59701
|
function cacheToken(tok) {
|
|
59390
59702
|
const c2 = readTokenCache();
|
|
59391
59703
|
c2[cacheKey] = tok;
|
|
59392
|
-
|
|
59393
|
-
|
|
59704
|
+
fs34.mkdirSync(path16.dirname(TOKEN_CACHE()), { recursive: true });
|
|
59705
|
+
fs34.writeFileSync(TOKEN_CACHE(), JSON.stringify(c2, null, 2));
|
|
59394
59706
|
}
|
|
59395
59707
|
var clientPromise = null;
|
|
59396
59708
|
var inbox = [];
|
|
@@ -59445,6 +59757,7 @@ function ensureClient() {
|
|
|
59445
59757
|
};
|
|
59446
59758
|
const cleanup = [];
|
|
59447
59759
|
const provision = () => JefriClient.connect({
|
|
59760
|
+
registeredRecovery: DESKTOP_RECOVERY,
|
|
59448
59761
|
server: hubUrl(),
|
|
59449
59762
|
username: USERNAME,
|
|
59450
59763
|
name: NAME,
|
|
@@ -59457,7 +59770,7 @@ function ensureClient() {
|
|
|
59457
59770
|
});
|
|
59458
59771
|
let c2;
|
|
59459
59772
|
if (known) {
|
|
59460
|
-
c2 = await JefriClient.connect({ server: hubUrl(), token: known, status: "online", clientInfo, takeoverTicket: LIVE_DESTINATION ? void 0 : process.env.JEFRI_TAKEOVER_TICKET });
|
|
59773
|
+
c2 = await JefriClient.connect({ registeredRecovery: DESKTOP_RECOVERY, server: hubUrl(), token: known, status: "online", clientInfo, takeoverTicket: LIVE_DESTINATION ? void 0 : process.env.JEFRI_TAKEOVER_TICKET });
|
|
59461
59774
|
if (!c2.identity) {
|
|
59462
59775
|
if (TOKEN) throw new Error("JEFRI_TOKEN was rejected by the server");
|
|
59463
59776
|
if (!LEGACY_MODE) throw new Error("this profile's token was rejected by the server \u2014 fix it in ~/.jefri/identities.json");
|
|
@@ -60213,6 +60526,10 @@ async function main() {
|
|
|
60213
60526
|
const bootConnect = (attempt = 0) => {
|
|
60214
60527
|
if (liveHold || liveMessage) return;
|
|
60215
60528
|
ensureClient().catch((e) => {
|
|
60529
|
+
if (DESKTOP_RECOVERY && terminalConnectionCode(e)) {
|
|
60530
|
+
log2(`initial connect stopped (${terminalConnectionCode(e)}):`, e?.message ?? e);
|
|
60531
|
+
return;
|
|
60532
|
+
}
|
|
60216
60533
|
const delay = Math.min(2e3 * 2 ** attempt, 3e4);
|
|
60217
60534
|
const jittered = Math.floor(delay / 2 + Math.random() * (delay / 2));
|
|
60218
60535
|
log2(`initial connect failed (retrying in ${Math.round(jittered / 1e3)}s):`, e?.message ?? e);
|