@essentialai/cogent-plugin 3.14.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bridge/cogent-bridge.mjs +546 -72
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Inter-session communication bridge for Claude Code with Slack integration. Enables CC agents and Slack team members to communicate in real time.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Essential AI Solutions",
|
package/bridge/cogent-bridge.mjs
CHANGED
|
@@ -152,31 +152,33 @@ var init_detect = __esm({
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
// src/wizard/templates.ts
|
|
155
|
-
function
|
|
155
|
+
function cogentEnv(mode) {
|
|
156
|
+
const env = {
|
|
157
|
+
COGENT_LOG_LEVEL: "info",
|
|
158
|
+
COGENT_TIMEOUT_MS: "300000"
|
|
159
|
+
};
|
|
160
|
+
if (mode === "local") env.COGENT_LOCAL = "1";
|
|
161
|
+
return env;
|
|
162
|
+
}
|
|
163
|
+
function mcpJsonContent(npxPath, mode = "cloud") {
|
|
156
164
|
const config2 = {
|
|
157
165
|
mcpServers: {
|
|
158
166
|
"cogent": {
|
|
159
167
|
command: npxPath,
|
|
160
168
|
args: ["-y", "@essentialai/cogent-bridge"],
|
|
161
|
-
env:
|
|
162
|
-
COGENT_LOG_LEVEL: "info",
|
|
163
|
-
COGENT_TIMEOUT_MS: "300000"
|
|
164
|
-
}
|
|
169
|
+
env: cogentEnv(mode)
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
};
|
|
168
173
|
return JSON.stringify(config2, null, 2) + "\n";
|
|
169
174
|
}
|
|
170
|
-
function mcpJsonMerge(existing, npxPath) {
|
|
175
|
+
function mcpJsonMerge(existing, npxPath, mode = "cloud") {
|
|
171
176
|
const parsed = JSON.parse(existing);
|
|
172
177
|
if (!parsed.mcpServers) parsed.mcpServers = {};
|
|
173
178
|
parsed.mcpServers["cogent"] = {
|
|
174
179
|
command: npxPath,
|
|
175
180
|
args: ["-y", "@essentialai/cogent-bridge"],
|
|
176
|
-
env:
|
|
177
|
-
COGENT_LOG_LEVEL: "info",
|
|
178
|
-
COGENT_TIMEOUT_MS: "300000"
|
|
179
|
-
}
|
|
181
|
+
env: cogentEnv(mode)
|
|
180
182
|
};
|
|
181
183
|
return JSON.stringify(parsed, null, 2) + "\n";
|
|
182
184
|
}
|
|
@@ -380,14 +382,14 @@ var init_scaffold_demo = __esm({
|
|
|
380
382
|
// src/wizard/scaffold-real.ts
|
|
381
383
|
import fs2 from "node:fs";
|
|
382
384
|
import path2 from "node:path";
|
|
383
|
-
function writeMcpJson(projectPath, npxPath) {
|
|
385
|
+
function writeMcpJson(projectPath, npxPath, mode) {
|
|
384
386
|
const filePath = path2.join(projectPath, ".mcp.json");
|
|
385
387
|
if (fs2.existsSync(filePath)) {
|
|
386
388
|
const existing = fs2.readFileSync(filePath, "utf-8");
|
|
387
|
-
fs2.writeFileSync(filePath, mcpJsonMerge(existing, npxPath), "utf-8");
|
|
389
|
+
fs2.writeFileSync(filePath, mcpJsonMerge(existing, npxPath, mode), "utf-8");
|
|
388
390
|
return "modified";
|
|
389
391
|
}
|
|
390
|
-
fs2.writeFileSync(filePath, mcpJsonContent(npxPath), "utf-8");
|
|
392
|
+
fs2.writeFileSync(filePath, mcpJsonContent(npxPath, mode), "utf-8");
|
|
391
393
|
return "created";
|
|
392
394
|
}
|
|
393
395
|
function writeClaudeMd(projectPath, peerId, label, otherPeerId) {
|
|
@@ -408,7 +410,8 @@ function scaffoldReal(config2) {
|
|
|
408
410
|
const created = [];
|
|
409
411
|
const modified = [];
|
|
410
412
|
const skipped = [];
|
|
411
|
-
const
|
|
413
|
+
const mode = config2.mode ?? "cloud";
|
|
414
|
+
const mcpA = writeMcpJson(config2.projectAPath, config2.npxPath, mode);
|
|
412
415
|
const mcpAPath = path2.join(config2.projectAPath, ".mcp.json");
|
|
413
416
|
if (mcpA === "created") created.push(mcpAPath);
|
|
414
417
|
else modified.push(mcpAPath);
|
|
@@ -422,7 +425,7 @@ function scaffoldReal(config2) {
|
|
|
422
425
|
if (claudeA === "created") created.push(claudeAPath);
|
|
423
426
|
else if (claudeA === "modified") modified.push(claudeAPath);
|
|
424
427
|
else skipped.push(claudeAPath);
|
|
425
|
-
const mcpB = writeMcpJson(config2.projectBPath, config2.npxPath);
|
|
428
|
+
const mcpB = writeMcpJson(config2.projectBPath, config2.npxPath, mode);
|
|
426
429
|
const mcpBPath = path2.join(config2.projectBPath, ".mcp.json");
|
|
427
430
|
if (mcpB === "created") created.push(mcpBPath);
|
|
428
431
|
else modified.push(mcpBPath);
|
|
@@ -565,6 +568,11 @@ async function runWizard() {
|
|
|
565
568
|
const idB = await ask(rl, "Peer ID for Project B:", defaultIdB);
|
|
566
569
|
const defaultLabelB = "CC_" + path3.basename(pathB).replace(/[^a-zA-Z0-9]/g, "_");
|
|
567
570
|
const labelB = await ask(rl, "Label for Project B:", defaultLabelB);
|
|
571
|
+
const modeIdx = await choose(rl, "How should these agents connect?", [
|
|
572
|
+
"Cloud \u2014 collaborate with remote agents over cogent.tools (recommended)",
|
|
573
|
+
"Local \u2014 offline, this machine only (self-hosted / local-LLM / air-gapped)"
|
|
574
|
+
]);
|
|
575
|
+
const mode2 = modeIdx === 1 ? "local" : "cloud";
|
|
568
576
|
heading("Configuring projects");
|
|
569
577
|
const result = scaffoldReal({
|
|
570
578
|
projectAPath: path3.resolve(pathA),
|
|
@@ -573,7 +581,8 @@ async function runWizard() {
|
|
|
573
581
|
projectBPath: path3.resolve(pathB),
|
|
574
582
|
projectBId: idB,
|
|
575
583
|
projectBLabel: labelB,
|
|
576
|
-
npxPath
|
|
584
|
+
npxPath,
|
|
585
|
+
mode: mode2
|
|
577
586
|
});
|
|
578
587
|
printRealNextSteps(path3.resolve(pathA), idA, labelA, path3.resolve(pathB), idB, labelB, result);
|
|
579
588
|
}
|
|
@@ -1250,8 +1259,8 @@ var init_parseUtil = __esm({
|
|
|
1250
1259
|
init_errors();
|
|
1251
1260
|
init_en();
|
|
1252
1261
|
makeIssue = (params) => {
|
|
1253
|
-
const { data, path:
|
|
1254
|
-
const fullPath = [...
|
|
1262
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
1263
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
1255
1264
|
const fullIssue = {
|
|
1256
1265
|
...issueData,
|
|
1257
1266
|
path: fullPath
|
|
@@ -1531,11 +1540,11 @@ var init_types = __esm({
|
|
|
1531
1540
|
init_parseUtil();
|
|
1532
1541
|
init_util();
|
|
1533
1542
|
ParseInputLazyPath = class {
|
|
1534
|
-
constructor(parent, value,
|
|
1543
|
+
constructor(parent, value, path16, key) {
|
|
1535
1544
|
this._cachedPath = [];
|
|
1536
1545
|
this.parent = parent;
|
|
1537
1546
|
this.data = value;
|
|
1538
|
-
this._path =
|
|
1547
|
+
this._path = path16;
|
|
1539
1548
|
this._key = key;
|
|
1540
1549
|
}
|
|
1541
1550
|
get path() {
|
|
@@ -4987,10 +4996,10 @@ function assignProp(target, prop, value) {
|
|
|
4987
4996
|
configurable: true
|
|
4988
4997
|
});
|
|
4989
4998
|
}
|
|
4990
|
-
function getElementAtPath(obj,
|
|
4991
|
-
if (!
|
|
4999
|
+
function getElementAtPath(obj, path16) {
|
|
5000
|
+
if (!path16)
|
|
4992
5001
|
return obj;
|
|
4993
|
-
return
|
|
5002
|
+
return path16.reduce((acc, key) => acc?.[key], obj);
|
|
4994
5003
|
}
|
|
4995
5004
|
function promiseAllObject(promisesObj) {
|
|
4996
5005
|
const keys = Object.keys(promisesObj);
|
|
@@ -5239,11 +5248,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5239
5248
|
}
|
|
5240
5249
|
return false;
|
|
5241
5250
|
}
|
|
5242
|
-
function prefixIssues(
|
|
5251
|
+
function prefixIssues(path16, issues) {
|
|
5243
5252
|
return issues.map((iss) => {
|
|
5244
5253
|
var _a;
|
|
5245
5254
|
(_a = iss).path ?? (_a.path = []);
|
|
5246
|
-
iss.path.unshift(
|
|
5255
|
+
iss.path.unshift(path16);
|
|
5247
5256
|
return iss;
|
|
5248
5257
|
});
|
|
5249
5258
|
}
|
|
@@ -17049,8 +17058,8 @@ var require_utils = __commonJS({
|
|
|
17049
17058
|
}
|
|
17050
17059
|
return ind;
|
|
17051
17060
|
}
|
|
17052
|
-
function removeDotSegments(
|
|
17053
|
-
let input =
|
|
17061
|
+
function removeDotSegments(path16) {
|
|
17062
|
+
let input = path16;
|
|
17054
17063
|
const output = [];
|
|
17055
17064
|
let nextSlash = -1;
|
|
17056
17065
|
let len = 0;
|
|
@@ -17249,8 +17258,8 @@ var require_schemes = __commonJS({
|
|
|
17249
17258
|
wsComponent.secure = void 0;
|
|
17250
17259
|
}
|
|
17251
17260
|
if (wsComponent.resourceName) {
|
|
17252
|
-
const [
|
|
17253
|
-
wsComponent.path =
|
|
17261
|
+
const [path16, query] = wsComponent.resourceName.split("?");
|
|
17262
|
+
wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
|
|
17254
17263
|
wsComponent.query = query;
|
|
17255
17264
|
wsComponent.resourceName = void 0;
|
|
17256
17265
|
}
|
|
@@ -20603,12 +20612,12 @@ var require_dist = __commonJS({
|
|
|
20603
20612
|
throw new Error(`Unknown format "${name}"`);
|
|
20604
20613
|
return f;
|
|
20605
20614
|
};
|
|
20606
|
-
function addFormats(ajv, list,
|
|
20615
|
+
function addFormats(ajv, list, fs15, exportName) {
|
|
20607
20616
|
var _a;
|
|
20608
20617
|
var _b;
|
|
20609
20618
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
20610
20619
|
for (const f of list)
|
|
20611
|
-
ajv.addFormat(f,
|
|
20620
|
+
ajv.addFormat(f, fs15[f]);
|
|
20612
20621
|
}
|
|
20613
20622
|
module.exports = exports = formatsPlugin;
|
|
20614
20623
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -21720,8 +21729,8 @@ var require_parseUtil = __commonJS({
|
|
|
21720
21729
|
var errors_js_1 = require_errors2();
|
|
21721
21730
|
var en_js_1 = __importDefault(require_en());
|
|
21722
21731
|
var makeIssue2 = (params) => {
|
|
21723
|
-
const { data, path:
|
|
21724
|
-
const fullPath = [...
|
|
21732
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
21733
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
21725
21734
|
const fullIssue = {
|
|
21726
21735
|
...issueData,
|
|
21727
21736
|
path: fullPath
|
|
@@ -21875,11 +21884,11 @@ var require_types2 = __commonJS({
|
|
|
21875
21884
|
var parseUtil_js_1 = require_parseUtil();
|
|
21876
21885
|
var util_js_1 = require_util2();
|
|
21877
21886
|
var ParseInputLazyPath2 = class {
|
|
21878
|
-
constructor(parent, value,
|
|
21887
|
+
constructor(parent, value, path16, key) {
|
|
21879
21888
|
this._cachedPath = [];
|
|
21880
21889
|
this.parent = parent;
|
|
21881
21890
|
this.data = value;
|
|
21882
|
-
this._path =
|
|
21891
|
+
this._path = path16;
|
|
21883
21892
|
this._key = key;
|
|
21884
21893
|
}
|
|
21885
21894
|
get path() {
|
|
@@ -25468,10 +25477,10 @@ var require_zod = __commonJS({
|
|
|
25468
25477
|
};
|
|
25469
25478
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25470
25479
|
exports.z = void 0;
|
|
25471
|
-
var
|
|
25472
|
-
exports.z =
|
|
25480
|
+
var z18 = __importStar(require_external());
|
|
25481
|
+
exports.z = z18;
|
|
25473
25482
|
__exportStar(require_external(), exports);
|
|
25474
|
-
exports.default =
|
|
25483
|
+
exports.default = z18;
|
|
25475
25484
|
}
|
|
25476
25485
|
});
|
|
25477
25486
|
|
|
@@ -26380,8 +26389,8 @@ var init_stdio2 = __esm({
|
|
|
26380
26389
|
// src/constants.ts
|
|
26381
26390
|
import { createRequire } from "node:module";
|
|
26382
26391
|
function resolveVersion() {
|
|
26383
|
-
if ("3.
|
|
26384
|
-
return "3.
|
|
26392
|
+
if ("3.15.0") {
|
|
26393
|
+
return "3.15.0";
|
|
26385
26394
|
}
|
|
26386
26395
|
try {
|
|
26387
26396
|
const require2 = createRequire(import.meta.url);
|
|
@@ -27553,7 +27562,7 @@ var init_http_backend = __esm({
|
|
|
27553
27562
|
* in cloud mode we ignore it and use this.sessionId (the cloud session).
|
|
27554
27563
|
*/
|
|
27555
27564
|
async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
|
|
27556
|
-
const
|
|
27565
|
+
const path16 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27557
27566
|
const body = {
|
|
27558
27567
|
peerId,
|
|
27559
27568
|
cwd,
|
|
@@ -27581,7 +27590,7 @@ var init_http_backend = __esm({
|
|
|
27581
27590
|
body.role = roleCap.slice("role:".length);
|
|
27582
27591
|
}
|
|
27583
27592
|
}
|
|
27584
|
-
return this.http.post(
|
|
27593
|
+
return this.http.post(path16, body);
|
|
27585
27594
|
}
|
|
27586
27595
|
/**
|
|
27587
27596
|
* Deregister a peer from the cloud session.
|
|
@@ -27590,9 +27599,9 @@ var init_http_backend = __esm({
|
|
|
27590
27599
|
* Returns true on success, false if the peer was not found.
|
|
27591
27600
|
*/
|
|
27592
27601
|
async deregisterPeer(peerId) {
|
|
27593
|
-
const
|
|
27602
|
+
const path16 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
|
|
27594
27603
|
try {
|
|
27595
|
-
await this.http.delete(
|
|
27604
|
+
await this.http.delete(path16, {});
|
|
27596
27605
|
return true;
|
|
27597
27606
|
} catch {
|
|
27598
27607
|
return false;
|
|
@@ -27612,8 +27621,8 @@ var init_http_backend = __esm({
|
|
|
27612
27621
|
* GET /api/sessions/:sessionId/peers
|
|
27613
27622
|
*/
|
|
27614
27623
|
async listPeers() {
|
|
27615
|
-
const
|
|
27616
|
-
const result = await this.http.get(
|
|
27624
|
+
const path16 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27625
|
+
const result = await this.http.get(path16);
|
|
27617
27626
|
return result.peers;
|
|
27618
27627
|
}
|
|
27619
27628
|
/**
|
|
@@ -27621,8 +27630,8 @@ var init_http_backend = __esm({
|
|
|
27621
27630
|
* Calls the lightweight heartbeat endpoint on the cloud relay server.
|
|
27622
27631
|
*/
|
|
27623
27632
|
async updateLastSeen(peerId) {
|
|
27624
|
-
const
|
|
27625
|
-
await this.http.post(
|
|
27633
|
+
const path16 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
|
|
27634
|
+
await this.http.post(path16, { peerId });
|
|
27626
27635
|
}
|
|
27627
27636
|
/**
|
|
27628
27637
|
* Record a message in the cloud session.
|
|
@@ -27633,7 +27642,7 @@ var init_http_backend = __esm({
|
|
|
27633
27642
|
* the input record to construct a full MessageRecord.
|
|
27634
27643
|
*/
|
|
27635
27644
|
async recordMessage(record2) {
|
|
27636
|
-
const
|
|
27645
|
+
const path16 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27637
27646
|
const body = {
|
|
27638
27647
|
fromPeerId: record2.fromPeerId,
|
|
27639
27648
|
toPeerId: record2.toPeerId,
|
|
@@ -27642,7 +27651,7 @@ var init_http_backend = __esm({
|
|
|
27642
27651
|
if (record2.isRelayEcho === true) {
|
|
27643
27652
|
body.isRelayEcho = true;
|
|
27644
27653
|
}
|
|
27645
|
-
const resp = await this.http.post(
|
|
27654
|
+
const resp = await this.http.post(path16, body);
|
|
27646
27655
|
return {
|
|
27647
27656
|
...record2,
|
|
27648
27657
|
id: resp.id,
|
|
@@ -27656,7 +27665,7 @@ var init_http_backend = __esm({
|
|
|
27656
27665
|
* Supports optional peerId filter and limit (default 300).
|
|
27657
27666
|
*/
|
|
27658
27667
|
async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
|
|
27659
|
-
const
|
|
27668
|
+
const path16 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27660
27669
|
const query = {};
|
|
27661
27670
|
if (peerId) {
|
|
27662
27671
|
query.peerId = peerId;
|
|
@@ -27671,7 +27680,7 @@ var init_http_backend = __esm({
|
|
|
27671
27680
|
if (dedupInferredEchoes) {
|
|
27672
27681
|
query.dedupInferredEchoes = "true";
|
|
27673
27682
|
}
|
|
27674
|
-
const result = await this.http.get(
|
|
27683
|
+
const result = await this.http.get(path16, query);
|
|
27675
27684
|
return result.messages;
|
|
27676
27685
|
}
|
|
27677
27686
|
/**
|
|
@@ -27682,12 +27691,12 @@ var init_http_backend = __esm({
|
|
|
27682
27691
|
* so that get-history shows the actual response (not null).
|
|
27683
27692
|
*/
|
|
27684
27693
|
async updateMessageResponse(messageId, response, durationMs, pending) {
|
|
27685
|
-
const
|
|
27694
|
+
const path16 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
|
|
27686
27695
|
const body = { response, durationMs };
|
|
27687
27696
|
if (pending === true) {
|
|
27688
27697
|
body.pending = true;
|
|
27689
27698
|
}
|
|
27690
|
-
await this.http.patch(
|
|
27699
|
+
await this.http.patch(path16, body);
|
|
27691
27700
|
}
|
|
27692
27701
|
/**
|
|
27693
27702
|
* Validate a session.
|
|
@@ -27829,8 +27838,8 @@ var init_http_client = __esm({
|
|
|
27829
27838
|
* Perform an authenticated GET request.
|
|
27830
27839
|
* Appends query parameters to the URL if provided.
|
|
27831
27840
|
*/
|
|
27832
|
-
async get(
|
|
27833
|
-
const url = new URL(
|
|
27841
|
+
async get(path16, query) {
|
|
27842
|
+
const url = new URL(path16, this.baseUrl);
|
|
27834
27843
|
if (query) {
|
|
27835
27844
|
for (const [k, v] of Object.entries(query)) {
|
|
27836
27845
|
url.searchParams.set(k, v);
|
|
@@ -27845,8 +27854,8 @@ var init_http_client = __esm({
|
|
|
27845
27854
|
/**
|
|
27846
27855
|
* Perform an authenticated POST request with a JSON body.
|
|
27847
27856
|
*/
|
|
27848
|
-
async post(
|
|
27849
|
-
const url = new URL(
|
|
27857
|
+
async post(path16, body) {
|
|
27858
|
+
const url = new URL(path16, this.baseUrl);
|
|
27850
27859
|
const resp = await this.fetchWithTimeout(url, {
|
|
27851
27860
|
method: "POST",
|
|
27852
27861
|
headers: this.headers(),
|
|
@@ -27864,8 +27873,8 @@ var init_http_client = __esm({
|
|
|
27864
27873
|
* Perform an authenticated PATCH request with a JSON body.
|
|
27865
27874
|
* Used to update existing resources (e.g., message response fields).
|
|
27866
27875
|
*/
|
|
27867
|
-
async patch(
|
|
27868
|
-
const url = new URL(
|
|
27876
|
+
async patch(path16, body) {
|
|
27877
|
+
const url = new URL(path16, this.baseUrl);
|
|
27869
27878
|
const resp = await this.fetchWithTimeout(url, {
|
|
27870
27879
|
method: "PATCH",
|
|
27871
27880
|
headers: this.headers(),
|
|
@@ -27888,8 +27897,8 @@ var init_http_client = __esm({
|
|
|
27888
27897
|
* "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
|
|
27889
27898
|
* any client (including third-party tools) DELETEd a peer without a body.
|
|
27890
27899
|
*/
|
|
27891
|
-
async delete(
|
|
27892
|
-
const url = new URL(
|
|
27900
|
+
async delete(path16, body) {
|
|
27901
|
+
const url = new URL(path16, this.baseUrl);
|
|
27893
27902
|
const headers = {
|
|
27894
27903
|
"Authorization": `Bearer ${this.token}`
|
|
27895
27904
|
};
|
|
@@ -32068,14 +32077,14 @@ var init_ws_client = __esm({
|
|
|
32068
32077
|
if (this.pollTimer) return;
|
|
32069
32078
|
const poll = async () => {
|
|
32070
32079
|
try {
|
|
32071
|
-
const
|
|
32080
|
+
const path16 = `/api/sessions/${this.opts.sessionId}/poll`;
|
|
32072
32081
|
const query = {
|
|
32073
32082
|
peerId: this.opts.peerId
|
|
32074
32083
|
};
|
|
32075
32084
|
if (this.lastMessageId) {
|
|
32076
32085
|
query.lastMessageId = this.lastMessageId;
|
|
32077
32086
|
}
|
|
32078
|
-
const result = await this.opts.http.get(
|
|
32087
|
+
const result = await this.opts.http.get(path16, query);
|
|
32079
32088
|
if (result.messages && result.messages.length > 0) {
|
|
32080
32089
|
this.opts.onMessages(result.messages);
|
|
32081
32090
|
this.lastMessageId = result.messages[result.messages.length - 1].id;
|
|
@@ -33873,8 +33882,61 @@ var init_startup = __esm({
|
|
|
33873
33882
|
}
|
|
33874
33883
|
});
|
|
33875
33884
|
|
|
33876
|
-
// src/
|
|
33885
|
+
// src/cloud/mail-credential-store.ts
|
|
33877
33886
|
import crypto3 from "node:crypto";
|
|
33887
|
+
import fs12 from "node:fs/promises";
|
|
33888
|
+
import path12 from "node:path";
|
|
33889
|
+
import os7 from "node:os";
|
|
33890
|
+
function defaultMailCredentialPath(cwd = process.cwd()) {
|
|
33891
|
+
const hash = crypto3.createHash("sha256").update(path12.resolve(cwd)).digest("hex").slice(0, 16);
|
|
33892
|
+
return path12.join(os7.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
|
|
33893
|
+
}
|
|
33894
|
+
function resolveMailCredentialPath(credentialPath) {
|
|
33895
|
+
if (credentialPath) return credentialPath;
|
|
33896
|
+
const envOverride = process.env.COGENT_MAIL_CREDENTIALS_FILE;
|
|
33897
|
+
if (envOverride) return envOverride;
|
|
33898
|
+
return defaultMailCredentialPath();
|
|
33899
|
+
}
|
|
33900
|
+
async function loadMailCredentials(credentialPath) {
|
|
33901
|
+
const filePath = resolveMailCredentialPath(credentialPath);
|
|
33902
|
+
try {
|
|
33903
|
+
return JSON.parse(await fs12.readFile(filePath, "utf-8"));
|
|
33904
|
+
} catch {
|
|
33905
|
+
return null;
|
|
33906
|
+
}
|
|
33907
|
+
}
|
|
33908
|
+
async function saveMailCredentials(creds, credentialPath) {
|
|
33909
|
+
const filePath = resolveMailCredentialPath(credentialPath);
|
|
33910
|
+
await fs12.mkdir(path12.dirname(filePath), { recursive: true, mode: 448 });
|
|
33911
|
+
await fs12.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
33912
|
+
await fs12.chmod(filePath, 384);
|
|
33913
|
+
}
|
|
33914
|
+
async function persistProvisionedMailbox(mailbox, credentialPath) {
|
|
33915
|
+
if (!mailbox?.address || !mailbox.password) return false;
|
|
33916
|
+
await saveMailCredentials(
|
|
33917
|
+
{
|
|
33918
|
+
address: mailbox.address,
|
|
33919
|
+
password: mailbox.password,
|
|
33920
|
+
imapHost: MAIL_DEFAULT_HOST,
|
|
33921
|
+
imapPort: 993,
|
|
33922
|
+
smtpHost: MAIL_DEFAULT_HOST,
|
|
33923
|
+
smtpPort: 465,
|
|
33924
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
33925
|
+
},
|
|
33926
|
+
credentialPath
|
|
33927
|
+
);
|
|
33928
|
+
return true;
|
|
33929
|
+
}
|
|
33930
|
+
var MAIL_DEFAULT_HOST;
|
|
33931
|
+
var init_mail_credential_store = __esm({
|
|
33932
|
+
"src/cloud/mail-credential-store.ts"() {
|
|
33933
|
+
"use strict";
|
|
33934
|
+
MAIL_DEFAULT_HOST = "mail.cogent.tools";
|
|
33935
|
+
}
|
|
33936
|
+
});
|
|
33937
|
+
|
|
33938
|
+
// src/tools/register-peer.ts
|
|
33939
|
+
import crypto4 from "node:crypto";
|
|
33878
33940
|
function registerRegisterPeerTool(server) {
|
|
33879
33941
|
server.registerTool(
|
|
33880
33942
|
"cogent_register_peer",
|
|
@@ -33933,7 +33995,7 @@ function registerRegisterPeerTool(server) {
|
|
|
33933
33995
|
}
|
|
33934
33996
|
}
|
|
33935
33997
|
if (!cloudSessionId) {
|
|
33936
|
-
autoSecret =
|
|
33998
|
+
autoSecret = crypto4.randomBytes(16).toString("hex");
|
|
33937
33999
|
const resp = await fetch(
|
|
33938
34000
|
`${endpoint}/api/sessions`,
|
|
33939
34001
|
{
|
|
@@ -34012,6 +34074,15 @@ function registerRegisterPeerTool(server) {
|
|
|
34012
34074
|
);
|
|
34013
34075
|
}
|
|
34014
34076
|
}
|
|
34077
|
+
try {
|
|
34078
|
+
if (await persistProvisionedMailbox(peer?.mailbox)) {
|
|
34079
|
+
logger.info(`Cogent Mail: saved auto-provisioned mailbox ${peer.mailbox?.address}`);
|
|
34080
|
+
}
|
|
34081
|
+
} catch (err) {
|
|
34082
|
+
logger.warn(
|
|
34083
|
+
`Cogent Mail: could not save auto-provisioned mailbox creds: ${err.message}`
|
|
34084
|
+
);
|
|
34085
|
+
}
|
|
34015
34086
|
const resolution = await resolveSession(
|
|
34016
34087
|
getConfig().COGENT_PLATFORM,
|
|
34017
34088
|
cwd,
|
|
@@ -34094,6 +34165,7 @@ var init_register_peer = __esm({
|
|
|
34094
34165
|
init_backend();
|
|
34095
34166
|
init_errors4();
|
|
34096
34167
|
init_credential_store();
|
|
34168
|
+
init_mail_credential_store();
|
|
34097
34169
|
init_logger();
|
|
34098
34170
|
init_heartbeat();
|
|
34099
34171
|
init_auto_relay();
|
|
@@ -34403,7 +34475,8 @@ var init_peer = __esm({
|
|
|
34403
34475
|
workspaceId: import_zod5.z.string().optional().describe("Durable workspace identifier (Slice 5) \u2014 stable id not tied to the mutable cwd hint"),
|
|
34404
34476
|
threadId: import_zod5.z.string().optional().describe("Durable thread/conversation identifier (Slice 5)"),
|
|
34405
34477
|
role: import_zod5.z.string().max(64).optional().describe("A2A role advertised by this peer (Agent Card)"),
|
|
34406
|
-
capabilities: import_zod5.z.array(import_zod5.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]")
|
|
34478
|
+
capabilities: import_zod5.z.array(import_zod5.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]"),
|
|
34479
|
+
mailboxAddress: import_zod5.z.string().max(320).optional().describe("Auto-provisioned Cogent Mail address (non-secret); NEVER the password")
|
|
34407
34480
|
});
|
|
34408
34481
|
}
|
|
34409
34482
|
});
|
|
@@ -34751,6 +34824,19 @@ function toSkills(peer) {
|
|
|
34751
34824
|
};
|
|
34752
34825
|
});
|
|
34753
34826
|
}
|
|
34827
|
+
function toMailboxExtensions(peer) {
|
|
34828
|
+
const address = peer.mailboxAddress;
|
|
34829
|
+
if (!address)
|
|
34830
|
+
return [];
|
|
34831
|
+
return [
|
|
34832
|
+
{
|
|
34833
|
+
uri: COGENT_MAILBOX_EXTENSION_URI,
|
|
34834
|
+
description: "Cogent Mail mailbox address (non-secret) for this peer.",
|
|
34835
|
+
required: false,
|
|
34836
|
+
params: { address }
|
|
34837
|
+
}
|
|
34838
|
+
];
|
|
34839
|
+
}
|
|
34754
34840
|
function synthDescription(peer) {
|
|
34755
34841
|
const parts = [`Cogent fabric peer on ${peer.platform ?? "cc"}`];
|
|
34756
34842
|
if (peer.role)
|
|
@@ -34766,6 +34852,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34766
34852
|
const version2 = ctx.clientVersion ?? peer.clientVersion ?? "0.0.0";
|
|
34767
34853
|
const provider = { organization: `Cogent \xB7 ${peer.platform ?? "cc"}`, url: "https://cogent.tools" };
|
|
34768
34854
|
const skills = toSkills(peer);
|
|
34855
|
+
const extensions = toMailboxExtensions(peer);
|
|
34769
34856
|
if (ctx.cardVersion === "0.3.0") {
|
|
34770
34857
|
return {
|
|
34771
34858
|
protocolVersion: "0.3.0",
|
|
@@ -34774,7 +34861,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34774
34861
|
url,
|
|
34775
34862
|
version: version2,
|
|
34776
34863
|
provider,
|
|
34777
|
-
capabilities: {},
|
|
34864
|
+
capabilities: extensions.length ? { extensions } : {},
|
|
34778
34865
|
defaultInputModes: ["text/plain"],
|
|
34779
34866
|
defaultOutputModes: ["text/plain"],
|
|
34780
34867
|
skills,
|
|
@@ -34795,7 +34882,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34795
34882
|
tenant: ctx.sessionId
|
|
34796
34883
|
}
|
|
34797
34884
|
],
|
|
34798
|
-
capabilities: { extensions
|
|
34885
|
+
capabilities: { extensions },
|
|
34799
34886
|
defaultInputModes: ["text/plain"],
|
|
34800
34887
|
defaultOutputModes: ["text/plain"],
|
|
34801
34888
|
skills,
|
|
@@ -34804,12 +34891,13 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34804
34891
|
signatures: []
|
|
34805
34892
|
};
|
|
34806
34893
|
}
|
|
34807
|
-
var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION;
|
|
34894
|
+
var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION, COGENT_MAILBOX_EXTENSION_URI;
|
|
34808
34895
|
var init_agent_card = __esm({
|
|
34809
34896
|
"cogent/dist/a2a/agent-card.js"() {
|
|
34810
34897
|
"use strict";
|
|
34811
34898
|
COGENT_PROTOCOL_BINDING = "COGENT-RELAY";
|
|
34812
34899
|
A2A_PROTOCOL_VERSION = "1.0";
|
|
34900
|
+
COGENT_MAILBOX_EXTENSION_URI = "https://cogent.tools/a2a/ext/mailbox";
|
|
34813
34901
|
}
|
|
34814
34902
|
});
|
|
34815
34903
|
|
|
@@ -35104,7 +35192,7 @@ var init_health_check2 = __esm({
|
|
|
35104
35192
|
});
|
|
35105
35193
|
|
|
35106
35194
|
// src/tools/create-session.ts
|
|
35107
|
-
import
|
|
35195
|
+
import crypto5 from "node:crypto";
|
|
35108
35196
|
function registerCreateSessionTool(server) {
|
|
35109
35197
|
server.registerTool(
|
|
35110
35198
|
"cogent_create_session",
|
|
@@ -35135,7 +35223,7 @@ function registerCreateSessionTool(server) {
|
|
|
35135
35223
|
)
|
|
35136
35224
|
);
|
|
35137
35225
|
}
|
|
35138
|
-
const sessionSecret = secret ??
|
|
35226
|
+
const sessionSecret = secret ?? crypto5.randomBytes(16).toString("hex");
|
|
35139
35227
|
const resp = await fetch(
|
|
35140
35228
|
`${config2.COGENT_ENDPOINT}/api/sessions`,
|
|
35141
35229
|
{
|
|
@@ -35363,6 +35451,386 @@ var init_join_session = __esm({
|
|
|
35363
35451
|
}
|
|
35364
35452
|
});
|
|
35365
35453
|
|
|
35454
|
+
// src/mail/validate.ts
|
|
35455
|
+
function isValidEmail(value) {
|
|
35456
|
+
return value.length <= 254 && EMAIL_RE.test(value);
|
|
35457
|
+
}
|
|
35458
|
+
function assertValidEmail(value, field) {
|
|
35459
|
+
if (!isValidEmail(value)) {
|
|
35460
|
+
throw new BridgeError(
|
|
35461
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35462
|
+
`${field} is not a valid email address: ${JSON.stringify(value)}`,
|
|
35463
|
+
`Provide a full address like agent-channel@mail.cogent.tools`
|
|
35464
|
+
);
|
|
35465
|
+
}
|
|
35466
|
+
}
|
|
35467
|
+
var EMAIL_RE;
|
|
35468
|
+
var init_validate = __esm({
|
|
35469
|
+
"src/mail/validate.ts"() {
|
|
35470
|
+
"use strict";
|
|
35471
|
+
init_errors4();
|
|
35472
|
+
EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
35473
|
+
}
|
|
35474
|
+
});
|
|
35475
|
+
|
|
35476
|
+
// src/tools/setup-mail.ts
|
|
35477
|
+
function registerSetupMailTool(server) {
|
|
35478
|
+
server.registerTool(
|
|
35479
|
+
"cogent_setup_mail",
|
|
35480
|
+
{
|
|
35481
|
+
title: "Configure this agent's mailbox",
|
|
35482
|
+
description: "Store the agent's mailbox address + password (from the admin Mail panel) locally so cogent_send_mail and cogent_fetch_mail can use them. Hosts default to mail.cogent.tools (IMAP 993 / SMTP 465).",
|
|
35483
|
+
inputSchema: {
|
|
35484
|
+
address: import_zod16.z.string().describe("Full mailbox address, e.g. claude-agent-backend@mail.cogent.tools"),
|
|
35485
|
+
password: import_zod16.z.string().describe("Mailbox password (shown once in the admin Mail panel on create/rotate)"),
|
|
35486
|
+
imapHost: import_zod16.z.string().optional().describe(`IMAP host (default ${DEFAULT_MAIL_HOST})`),
|
|
35487
|
+
imapPort: import_zod16.z.number().int().positive().optional().describe("IMAP port (default 993)"),
|
|
35488
|
+
smtpHost: import_zod16.z.string().optional().describe(`SMTP host (default ${DEFAULT_MAIL_HOST})`),
|
|
35489
|
+
smtpPort: import_zod16.z.number().int().positive().optional().describe("SMTP submission port (default 465)")
|
|
35490
|
+
},
|
|
35491
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
35492
|
+
},
|
|
35493
|
+
async ({ address, password, imapHost, imapPort, smtpHost, smtpPort }) => {
|
|
35494
|
+
try {
|
|
35495
|
+
assertValidEmail(address, "address");
|
|
35496
|
+
await saveMailCredentials({
|
|
35497
|
+
address,
|
|
35498
|
+
password,
|
|
35499
|
+
imapHost: imapHost ?? DEFAULT_MAIL_HOST,
|
|
35500
|
+
imapPort: imapPort ?? 993,
|
|
35501
|
+
smtpHost: smtpHost ?? DEFAULT_MAIL_HOST,
|
|
35502
|
+
smtpPort: smtpPort ?? 465,
|
|
35503
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35504
|
+
});
|
|
35505
|
+
return successResult({ configured: true, address });
|
|
35506
|
+
} catch (err) {
|
|
35507
|
+
return errorResult(err);
|
|
35508
|
+
}
|
|
35509
|
+
}
|
|
35510
|
+
);
|
|
35511
|
+
}
|
|
35512
|
+
var import_zod16, DEFAULT_MAIL_HOST;
|
|
35513
|
+
var init_setup_mail = __esm({
|
|
35514
|
+
"src/tools/setup-mail.ts"() {
|
|
35515
|
+
"use strict";
|
|
35516
|
+
import_zod16 = __toESM(require_zod(), 1);
|
|
35517
|
+
init_errors4();
|
|
35518
|
+
init_mail_credential_store();
|
|
35519
|
+
init_validate();
|
|
35520
|
+
DEFAULT_MAIL_HOST = MAIL_DEFAULT_HOST;
|
|
35521
|
+
}
|
|
35522
|
+
});
|
|
35523
|
+
|
|
35524
|
+
// src/mail/mail-sender.ts
|
|
35525
|
+
import fs13 from "node:fs/promises";
|
|
35526
|
+
import path13 from "node:path";
|
|
35527
|
+
async function buildAndSendMail(sender, creds, params) {
|
|
35528
|
+
const attachments = [];
|
|
35529
|
+
for (const p of params.attachmentPaths ?? []) {
|
|
35530
|
+
const content = await fs13.readFile(p);
|
|
35531
|
+
attachments.push({ filename: path13.basename(p), content });
|
|
35532
|
+
}
|
|
35533
|
+
const { messageId } = await sender.send({
|
|
35534
|
+
from: creds.address,
|
|
35535
|
+
to: params.to,
|
|
35536
|
+
subject: params.subject,
|
|
35537
|
+
text: params.text,
|
|
35538
|
+
attachments
|
|
35539
|
+
});
|
|
35540
|
+
return { messageId, from: creds.address, to: params.to };
|
|
35541
|
+
}
|
|
35542
|
+
var NodemailerMailSender;
|
|
35543
|
+
var init_mail_sender = __esm({
|
|
35544
|
+
"src/mail/mail-sender.ts"() {
|
|
35545
|
+
"use strict";
|
|
35546
|
+
NodemailerMailSender = class {
|
|
35547
|
+
constructor(creds, transportFactory) {
|
|
35548
|
+
this.creds = creds;
|
|
35549
|
+
this.transportFactory = transportFactory;
|
|
35550
|
+
}
|
|
35551
|
+
creds;
|
|
35552
|
+
transportFactory;
|
|
35553
|
+
async send(mail) {
|
|
35554
|
+
const transport = this.transportFactory ? await this.transportFactory() : await this.defaultTransport();
|
|
35555
|
+
const info = await transport.sendMail({
|
|
35556
|
+
from: mail.from,
|
|
35557
|
+
to: mail.to,
|
|
35558
|
+
subject: mail.subject,
|
|
35559
|
+
text: mail.text,
|
|
35560
|
+
attachments: mail.attachments.map((a) => ({ filename: a.filename, content: a.content }))
|
|
35561
|
+
});
|
|
35562
|
+
return { messageId: info.messageId ?? "" };
|
|
35563
|
+
}
|
|
35564
|
+
async defaultTransport() {
|
|
35565
|
+
const specifier = "nodemailer";
|
|
35566
|
+
const nodemailer = await import(specifier);
|
|
35567
|
+
return nodemailer.createTransport({
|
|
35568
|
+
host: this.creds.smtpHost,
|
|
35569
|
+
port: this.creds.smtpPort,
|
|
35570
|
+
secure: this.creds.smtpPort === 465,
|
|
35571
|
+
// implicit TLS on 465, STARTTLS on 587
|
|
35572
|
+
auth: { user: this.creds.address, pass: this.creds.password }
|
|
35573
|
+
});
|
|
35574
|
+
}
|
|
35575
|
+
};
|
|
35576
|
+
}
|
|
35577
|
+
});
|
|
35578
|
+
|
|
35579
|
+
// src/tools/send-mail.ts
|
|
35580
|
+
function registerSendMailTool(server, deps = {}) {
|
|
35581
|
+
const makeSender = deps.senderFactory ?? ((creds) => new NodemailerMailSender(creds));
|
|
35582
|
+
server.registerTool(
|
|
35583
|
+
"cogent_send_mail",
|
|
35584
|
+
{
|
|
35585
|
+
title: "Send an email from this agent's mailbox",
|
|
35586
|
+
description: "Send an email (with optional file attachments) from this agent's Cogent mailbox to another address. Requires cogent_setup_mail to have stored the mailbox credentials first.",
|
|
35587
|
+
inputSchema: {
|
|
35588
|
+
to: import_zod17.z.string().describe("Recipient email address, e.g. other-agent-backend@mail.cogent.tools"),
|
|
35589
|
+
subject: import_zod17.z.string().describe("Email subject"),
|
|
35590
|
+
body: import_zod17.z.string().describe("Plain-text email body"),
|
|
35591
|
+
attachments: import_zod17.z.array(import_zod17.z.string()).optional().describe("Optional local file paths to attach (each is read and attached by basename)")
|
|
35592
|
+
},
|
|
35593
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
35594
|
+
},
|
|
35595
|
+
async ({ to, subject, body, attachments }) => {
|
|
35596
|
+
try {
|
|
35597
|
+
assertValidEmail(to, "to");
|
|
35598
|
+
const creds = await loadMailCredentials();
|
|
35599
|
+
if (!creds) {
|
|
35600
|
+
return errorResult(
|
|
35601
|
+
new BridgeError(
|
|
35602
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35603
|
+
"No mailbox is configured for this agent",
|
|
35604
|
+
"Run cogent_setup_mail with the address + password from the admin Mail panel first"
|
|
35605
|
+
)
|
|
35606
|
+
);
|
|
35607
|
+
}
|
|
35608
|
+
const result = await buildAndSendMail(makeSender(creds), creds, {
|
|
35609
|
+
to: [to],
|
|
35610
|
+
subject,
|
|
35611
|
+
text: body,
|
|
35612
|
+
attachmentPaths: attachments
|
|
35613
|
+
});
|
|
35614
|
+
return successResult({ ...result, attachmentCount: attachments?.length ?? 0 });
|
|
35615
|
+
} catch (err) {
|
|
35616
|
+
return errorResult(err);
|
|
35617
|
+
}
|
|
35618
|
+
}
|
|
35619
|
+
);
|
|
35620
|
+
}
|
|
35621
|
+
var import_zod17;
|
|
35622
|
+
var init_send_mail = __esm({
|
|
35623
|
+
"src/tools/send-mail.ts"() {
|
|
35624
|
+
"use strict";
|
|
35625
|
+
import_zod17 = __toESM(require_zod(), 1);
|
|
35626
|
+
init_errors4();
|
|
35627
|
+
init_mail_credential_store();
|
|
35628
|
+
init_mail_sender();
|
|
35629
|
+
init_validate();
|
|
35630
|
+
}
|
|
35631
|
+
});
|
|
35632
|
+
|
|
35633
|
+
// src/mail/mail-fetcher.ts
|
|
35634
|
+
import fs14 from "node:fs/promises";
|
|
35635
|
+
import path14 from "node:path";
|
|
35636
|
+
async function saveAttachments(attachments, downloadDir) {
|
|
35637
|
+
if (attachments.length === 0) return [];
|
|
35638
|
+
await fs14.mkdir(downloadDir, { recursive: true });
|
|
35639
|
+
const saved = [];
|
|
35640
|
+
for (const a of attachments) {
|
|
35641
|
+
const name = path14.basename(a.filename) || "attachment";
|
|
35642
|
+
const savedPath = path14.join(downloadDir, name);
|
|
35643
|
+
await fs14.writeFile(savedPath, a.content);
|
|
35644
|
+
saved.push({ filename: name, savedPath, size: a.content.length });
|
|
35645
|
+
}
|
|
35646
|
+
return saved;
|
|
35647
|
+
}
|
|
35648
|
+
async function defaultImapSessionFactory(creds) {
|
|
35649
|
+
const specifier = "imapflow";
|
|
35650
|
+
const { ImapFlow } = await import(specifier);
|
|
35651
|
+
const client = new ImapFlow({
|
|
35652
|
+
host: creds.imapHost,
|
|
35653
|
+
port: creds.imapPort,
|
|
35654
|
+
secure: creds.imapPort === 993,
|
|
35655
|
+
auth: { user: creds.address, pass: creds.password },
|
|
35656
|
+
logger: false
|
|
35657
|
+
});
|
|
35658
|
+
await client.connect();
|
|
35659
|
+
await client.mailboxOpen("INBOX");
|
|
35660
|
+
return {
|
|
35661
|
+
async list(unreadOnly) {
|
|
35662
|
+
const criteria = unreadOnly ? { seen: false } : { all: true };
|
|
35663
|
+
const rows = [];
|
|
35664
|
+
for await (const msg of client.fetch(criteria, { uid: true, envelope: true, flags: true })) {
|
|
35665
|
+
const env = msg.envelope ?? {};
|
|
35666
|
+
rows.push({
|
|
35667
|
+
uid: msg.uid,
|
|
35668
|
+
from: env.from?.[0]?.address ?? "",
|
|
35669
|
+
subject: env.subject ?? "",
|
|
35670
|
+
date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
|
|
35671
|
+
unread: !hasFlag(msg.flags, "\\Seen")
|
|
35672
|
+
});
|
|
35673
|
+
}
|
|
35674
|
+
return rows.reverse();
|
|
35675
|
+
},
|
|
35676
|
+
async fetch(uid) {
|
|
35677
|
+
const msg = await client.fetchOne(uid, { uid: true, envelope: true, bodyStructure: true }, { uid: true });
|
|
35678
|
+
if (!msg) throw new Error(`no message with uid ${uid}`);
|
|
35679
|
+
const parts = flattenBodyStructure(msg.bodyStructure);
|
|
35680
|
+
let text = "";
|
|
35681
|
+
const attachments = [];
|
|
35682
|
+
for (const p of parts) {
|
|
35683
|
+
if (p.filename) {
|
|
35684
|
+
const dl = await client.download(uid, p.part, { uid: true });
|
|
35685
|
+
attachments.push({ filename: p.filename, content: await streamToBuffer(dl.content) });
|
|
35686
|
+
} else if (p.isText && !text) {
|
|
35687
|
+
const dl = await client.download(uid, p.part, { uid: true });
|
|
35688
|
+
text = (await streamToBuffer(dl.content)).toString("utf-8");
|
|
35689
|
+
}
|
|
35690
|
+
}
|
|
35691
|
+
const env = msg.envelope ?? {};
|
|
35692
|
+
return {
|
|
35693
|
+
from: env.from?.[0]?.address ?? "",
|
|
35694
|
+
subject: env.subject ?? "",
|
|
35695
|
+
date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
|
|
35696
|
+
text,
|
|
35697
|
+
attachments
|
|
35698
|
+
};
|
|
35699
|
+
},
|
|
35700
|
+
async close() {
|
|
35701
|
+
try {
|
|
35702
|
+
await client.logout();
|
|
35703
|
+
} catch {
|
|
35704
|
+
}
|
|
35705
|
+
}
|
|
35706
|
+
};
|
|
35707
|
+
}
|
|
35708
|
+
function flattenBodyStructure(node, acc = []) {
|
|
35709
|
+
if (!node || typeof node !== "object") return acc;
|
|
35710
|
+
const n = node;
|
|
35711
|
+
if (Array.isArray(n.childNodes) && n.childNodes.length > 0) {
|
|
35712
|
+
for (const child of n.childNodes) flattenBodyStructure(child, acc);
|
|
35713
|
+
return acc;
|
|
35714
|
+
}
|
|
35715
|
+
const type = (n.type ?? "").toLowerCase();
|
|
35716
|
+
const filename = n.dispositionParameters?.filename ?? n.parameters?.name;
|
|
35717
|
+
const isAttachment = (n.disposition ?? "").toLowerCase() === "attachment" || Boolean(filename);
|
|
35718
|
+
acc.push({
|
|
35719
|
+
part: n.part ?? "1",
|
|
35720
|
+
// single-part messages have no `part`; imapflow addresses them as "1"
|
|
35721
|
+
type,
|
|
35722
|
+
isText: type === "text/plain" && !isAttachment,
|
|
35723
|
+
filename: isAttachment ? filename : void 0
|
|
35724
|
+
});
|
|
35725
|
+
return acc;
|
|
35726
|
+
}
|
|
35727
|
+
function hasFlag(flags, flag) {
|
|
35728
|
+
if (flags instanceof Set) return flags.has(flag);
|
|
35729
|
+
if (Array.isArray(flags)) return flags.includes(flag);
|
|
35730
|
+
return false;
|
|
35731
|
+
}
|
|
35732
|
+
async function streamToBuffer(stream) {
|
|
35733
|
+
const chunks = [];
|
|
35734
|
+
if (!stream) return Buffer.alloc(0);
|
|
35735
|
+
for await (const chunk of stream) chunks.push(Buffer.from(chunk));
|
|
35736
|
+
return Buffer.concat(chunks);
|
|
35737
|
+
}
|
|
35738
|
+
var ImapflowMailFetcher;
|
|
35739
|
+
var init_mail_fetcher = __esm({
|
|
35740
|
+
"src/mail/mail-fetcher.ts"() {
|
|
35741
|
+
"use strict";
|
|
35742
|
+
ImapflowMailFetcher = class {
|
|
35743
|
+
constructor(creds, sessionFactory = defaultImapSessionFactory) {
|
|
35744
|
+
this.creds = creds;
|
|
35745
|
+
this.sessionFactory = sessionFactory;
|
|
35746
|
+
}
|
|
35747
|
+
creds;
|
|
35748
|
+
sessionFactory;
|
|
35749
|
+
async list(opts = {}) {
|
|
35750
|
+
const session = await this.sessionFactory(this.creds);
|
|
35751
|
+
try {
|
|
35752
|
+
let rows = await session.list(opts.unreadOnly ?? false);
|
|
35753
|
+
if (opts.limit && opts.limit > 0) rows = rows.slice(0, opts.limit);
|
|
35754
|
+
return rows;
|
|
35755
|
+
} finally {
|
|
35756
|
+
await session.close();
|
|
35757
|
+
}
|
|
35758
|
+
}
|
|
35759
|
+
async fetchOne(uid, downloadDir) {
|
|
35760
|
+
const session = await this.sessionFactory(this.creds);
|
|
35761
|
+
try {
|
|
35762
|
+
const c2 = await session.fetch(uid);
|
|
35763
|
+
const saved = await saveAttachments(c2.attachments, downloadDir);
|
|
35764
|
+
return { uid, from: c2.from, subject: c2.subject, date: c2.date, text: c2.text, attachments: saved };
|
|
35765
|
+
} finally {
|
|
35766
|
+
await session.close();
|
|
35767
|
+
}
|
|
35768
|
+
}
|
|
35769
|
+
};
|
|
35770
|
+
}
|
|
35771
|
+
});
|
|
35772
|
+
|
|
35773
|
+
// src/tools/fetch-mail.ts
|
|
35774
|
+
import os8 from "node:os";
|
|
35775
|
+
import path15 from "node:path";
|
|
35776
|
+
function registerFetchMailTool(server, deps = {}) {
|
|
35777
|
+
const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
|
|
35778
|
+
server.registerTool(
|
|
35779
|
+
"cogent_fetch_mail",
|
|
35780
|
+
{
|
|
35781
|
+
title: "Read this agent's mailbox",
|
|
35782
|
+
description: "List messages in this agent's Cogent mailbox, or fetch one message (text + attachments saved locally) by uid. Requires cogent_setup_mail to have stored the mailbox credentials.",
|
|
35783
|
+
inputSchema: {
|
|
35784
|
+
action: import_zod18.z.enum(["list", "fetch"]).optional().describe("'list' (default) or 'fetch' one message"),
|
|
35785
|
+
uid: import_zod18.z.number().int().positive().optional().describe("Message uid to fetch (required when action='fetch')"),
|
|
35786
|
+
unreadOnly: import_zod18.z.boolean().optional().describe("List only unread messages (default false)"),
|
|
35787
|
+
limit: import_zod18.z.number().int().positive().optional().describe("Max messages to list (default 25)"),
|
|
35788
|
+
downloadDir: import_zod18.z.string().optional().describe(`Directory to save fetched attachments (default ${DEFAULT_DOWNLOAD_DIR})`)
|
|
35789
|
+
},
|
|
35790
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
35791
|
+
},
|
|
35792
|
+
async ({ action, uid, unreadOnly, limit, downloadDir }) => {
|
|
35793
|
+
try {
|
|
35794
|
+
const creds = await loadMailCredentials();
|
|
35795
|
+
if (!creds) {
|
|
35796
|
+
return errorResult(
|
|
35797
|
+
new BridgeError(
|
|
35798
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35799
|
+
"No mailbox is configured for this agent",
|
|
35800
|
+
"Run cogent_setup_mail with the address + password from the admin Mail panel first"
|
|
35801
|
+
)
|
|
35802
|
+
);
|
|
35803
|
+
}
|
|
35804
|
+
const fetcher = makeFetcher(creds);
|
|
35805
|
+
if (action === "fetch") {
|
|
35806
|
+
if (uid === void 0) {
|
|
35807
|
+
return errorResult(
|
|
35808
|
+
new BridgeError("INVALID_INPUT" /* INVALID_INPUT */, "uid is required when action='fetch'", "Pass the uid from a cogent_fetch_mail list")
|
|
35809
|
+
);
|
|
35810
|
+
}
|
|
35811
|
+
const message = await fetcher.fetchOne(uid, downloadDir ?? DEFAULT_DOWNLOAD_DIR);
|
|
35812
|
+
return successResult({ mailbox: creds.address, message });
|
|
35813
|
+
}
|
|
35814
|
+
const messages = await fetcher.list({ unreadOnly, limit: limit ?? 25 });
|
|
35815
|
+
return successResult({ mailbox: creds.address, count: messages.length, messages });
|
|
35816
|
+
} catch (err) {
|
|
35817
|
+
return errorResult(err);
|
|
35818
|
+
}
|
|
35819
|
+
}
|
|
35820
|
+
);
|
|
35821
|
+
}
|
|
35822
|
+
var import_zod18, DEFAULT_DOWNLOAD_DIR;
|
|
35823
|
+
var init_fetch_mail = __esm({
|
|
35824
|
+
"src/tools/fetch-mail.ts"() {
|
|
35825
|
+
"use strict";
|
|
35826
|
+
import_zod18 = __toESM(require_zod(), 1);
|
|
35827
|
+
init_errors4();
|
|
35828
|
+
init_mail_credential_store();
|
|
35829
|
+
init_mail_fetcher();
|
|
35830
|
+
DEFAULT_DOWNLOAD_DIR = path15.join(os8.homedir(), ".cogent", "mail-downloads");
|
|
35831
|
+
}
|
|
35832
|
+
});
|
|
35833
|
+
|
|
35366
35834
|
// src/index.ts
|
|
35367
35835
|
var index_exports = {};
|
|
35368
35836
|
async function main() {
|
|
@@ -35376,6 +35844,9 @@ async function main() {
|
|
|
35376
35844
|
registerHealthCheckTool(server);
|
|
35377
35845
|
registerCreateSessionTool(server);
|
|
35378
35846
|
registerJoinSessionTool(server);
|
|
35847
|
+
registerSetupMailTool(server);
|
|
35848
|
+
registerSendMailTool(server);
|
|
35849
|
+
registerFetchMailTool(server);
|
|
35379
35850
|
if (cloudInbox) {
|
|
35380
35851
|
server.registerResource(
|
|
35381
35852
|
"cogent_inbox",
|
|
@@ -35435,6 +35906,9 @@ var init_index = __esm({
|
|
|
35435
35906
|
init_health_check2();
|
|
35436
35907
|
init_create_session();
|
|
35437
35908
|
init_join_session();
|
|
35909
|
+
init_setup_mail();
|
|
35910
|
+
init_send_mail();
|
|
35911
|
+
init_fetch_mail();
|
|
35438
35912
|
init_heartbeat();
|
|
35439
35913
|
process.on("uncaughtException", (err) => {
|
|
35440
35914
|
if (err.code === "EPIPE") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essentialai/cogent-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Cogent — Claude Code plugin (skills + slash-commands + MCP server) for the cross-agent comms fabric.",
|
|
5
5
|
"author": { "name": "Essential AI Solutions Ltd.", "url": "https://essentialai.uk" },
|
|
6
6
|
"homepage": "https://cogent.tools",
|