@essentialai/cogent-plugin 3.16.0 → 3.18.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 +325 -109
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.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
|
@@ -192,7 +192,7 @@ Your peer identity: **peerId=\`${peerId}\`**, **label=\`${label}\`**
|
|
|
192
192
|
### On Session Start
|
|
193
193
|
1. Find your Claude Code session ID:
|
|
194
194
|
\`\`\`bash
|
|
195
|
-
ls -t
|
|
195
|
+
ls -t "\${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/projects/$(pwd | sed 's/[^a-zA-Z0-9-]/-/g')/*.jsonl 2>/dev/null | head -1 | xargs -I{} basename {} .jsonl
|
|
196
196
|
\`\`\`
|
|
197
197
|
2. Register yourself on the bridge:
|
|
198
198
|
- Use \`cogent_register_peer\` with peerId="${peerId}", label="${label}"
|
|
@@ -1259,8 +1259,8 @@ var init_parseUtil = __esm({
|
|
|
1259
1259
|
init_errors();
|
|
1260
1260
|
init_en();
|
|
1261
1261
|
makeIssue = (params) => {
|
|
1262
|
-
const { data, path:
|
|
1263
|
-
const fullPath = [...
|
|
1262
|
+
const { data, path: path17, errorMaps, issueData } = params;
|
|
1263
|
+
const fullPath = [...path17, ...issueData.path || []];
|
|
1264
1264
|
const fullIssue = {
|
|
1265
1265
|
...issueData,
|
|
1266
1266
|
path: fullPath
|
|
@@ -1540,11 +1540,11 @@ var init_types = __esm({
|
|
|
1540
1540
|
init_parseUtil();
|
|
1541
1541
|
init_util();
|
|
1542
1542
|
ParseInputLazyPath = class {
|
|
1543
|
-
constructor(parent, value,
|
|
1543
|
+
constructor(parent, value, path17, key) {
|
|
1544
1544
|
this._cachedPath = [];
|
|
1545
1545
|
this.parent = parent;
|
|
1546
1546
|
this.data = value;
|
|
1547
|
-
this._path =
|
|
1547
|
+
this._path = path17;
|
|
1548
1548
|
this._key = key;
|
|
1549
1549
|
}
|
|
1550
1550
|
get path() {
|
|
@@ -4996,10 +4996,10 @@ function assignProp(target, prop, value) {
|
|
|
4996
4996
|
configurable: true
|
|
4997
4997
|
});
|
|
4998
4998
|
}
|
|
4999
|
-
function getElementAtPath(obj,
|
|
5000
|
-
if (!
|
|
4999
|
+
function getElementAtPath(obj, path17) {
|
|
5000
|
+
if (!path17)
|
|
5001
5001
|
return obj;
|
|
5002
|
-
return
|
|
5002
|
+
return path17.reduce((acc, key) => acc?.[key], obj);
|
|
5003
5003
|
}
|
|
5004
5004
|
function promiseAllObject(promisesObj) {
|
|
5005
5005
|
const keys = Object.keys(promisesObj);
|
|
@@ -5248,11 +5248,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5248
5248
|
}
|
|
5249
5249
|
return false;
|
|
5250
5250
|
}
|
|
5251
|
-
function prefixIssues(
|
|
5251
|
+
function prefixIssues(path17, issues) {
|
|
5252
5252
|
return issues.map((iss) => {
|
|
5253
5253
|
var _a;
|
|
5254
5254
|
(_a = iss).path ?? (_a.path = []);
|
|
5255
|
-
iss.path.unshift(
|
|
5255
|
+
iss.path.unshift(path17);
|
|
5256
5256
|
return iss;
|
|
5257
5257
|
});
|
|
5258
5258
|
}
|
|
@@ -17058,8 +17058,8 @@ var require_utils = __commonJS({
|
|
|
17058
17058
|
}
|
|
17059
17059
|
return ind;
|
|
17060
17060
|
}
|
|
17061
|
-
function removeDotSegments(
|
|
17062
|
-
let input =
|
|
17061
|
+
function removeDotSegments(path17) {
|
|
17062
|
+
let input = path17;
|
|
17063
17063
|
const output = [];
|
|
17064
17064
|
let nextSlash = -1;
|
|
17065
17065
|
let len = 0;
|
|
@@ -17258,8 +17258,8 @@ var require_schemes = __commonJS({
|
|
|
17258
17258
|
wsComponent.secure = void 0;
|
|
17259
17259
|
}
|
|
17260
17260
|
if (wsComponent.resourceName) {
|
|
17261
|
-
const [
|
|
17262
|
-
wsComponent.path =
|
|
17261
|
+
const [path17, query] = wsComponent.resourceName.split("?");
|
|
17262
|
+
wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
|
|
17263
17263
|
wsComponent.query = query;
|
|
17264
17264
|
wsComponent.resourceName = void 0;
|
|
17265
17265
|
}
|
|
@@ -21729,8 +21729,8 @@ var require_parseUtil = __commonJS({
|
|
|
21729
21729
|
var errors_js_1 = require_errors2();
|
|
21730
21730
|
var en_js_1 = __importDefault(require_en());
|
|
21731
21731
|
var makeIssue2 = (params) => {
|
|
21732
|
-
const { data, path:
|
|
21733
|
-
const fullPath = [...
|
|
21732
|
+
const { data, path: path17, errorMaps, issueData } = params;
|
|
21733
|
+
const fullPath = [...path17, ...issueData.path || []];
|
|
21734
21734
|
const fullIssue = {
|
|
21735
21735
|
...issueData,
|
|
21736
21736
|
path: fullPath
|
|
@@ -21884,11 +21884,11 @@ var require_types2 = __commonJS({
|
|
|
21884
21884
|
var parseUtil_js_1 = require_parseUtil();
|
|
21885
21885
|
var util_js_1 = require_util2();
|
|
21886
21886
|
var ParseInputLazyPath2 = class {
|
|
21887
|
-
constructor(parent, value,
|
|
21887
|
+
constructor(parent, value, path17, key) {
|
|
21888
21888
|
this._cachedPath = [];
|
|
21889
21889
|
this.parent = parent;
|
|
21890
21890
|
this.data = value;
|
|
21891
|
-
this._path =
|
|
21891
|
+
this._path = path17;
|
|
21892
21892
|
this._key = key;
|
|
21893
21893
|
}
|
|
21894
21894
|
get path() {
|
|
@@ -26389,8 +26389,8 @@ var init_stdio2 = __esm({
|
|
|
26389
26389
|
// src/constants.ts
|
|
26390
26390
|
import { createRequire } from "node:module";
|
|
26391
26391
|
function resolveVersion() {
|
|
26392
|
-
if ("3.
|
|
26393
|
-
return "3.
|
|
26392
|
+
if ("3.18.0") {
|
|
26393
|
+
return "3.18.0";
|
|
26394
26394
|
}
|
|
26395
26395
|
try {
|
|
26396
26396
|
const require2 = createRequire(import.meta.url);
|
|
@@ -26679,7 +26679,13 @@ var init_config = __esm({
|
|
|
26679
26679
|
// "human-broadcast" (toPeerId === "*" from a human origin: slack/gchat/web).
|
|
26680
26680
|
// Default both. Agent-origin broadcasts are never auto-surfaced by C (storm-safe;
|
|
26681
26681
|
// COGENT_BROADCAST_AWARENESS already handles seeing peer broadcasts).
|
|
26682
|
-
COGENT_CHECK_ON_STOP_SCOPE: import_zod2.z.string().default("directed,human-broadcast")
|
|
26682
|
+
COGENT_CHECK_ON_STOP_SCOPE: import_zod2.z.string().default("directed,human-broadcast"),
|
|
26683
|
+
// Cogent Mail F4 — the mailbox domains an agent may configure + send to. CSV; default is the
|
|
26684
|
+
// single Cogent mail domain. Cogent mail is ORGANISATION-ONLY (compliance: an accountable org
|
|
26685
|
+
// owns every mailbox), so BYO external / corporate-domain mailboxes are BLOCKED for now — this
|
|
26686
|
+
// allowlist is the configurable seam for adding per-org corporate domains in a future release.
|
|
26687
|
+
// Baked default here (a LIBRARY default, not plugin env) so Team agents get it too.
|
|
26688
|
+
COGENT_MAIL_ALLOWED_DOMAINS: import_zod2.z.string().default("mail.cogent.tools")
|
|
26683
26689
|
});
|
|
26684
26690
|
_config = null;
|
|
26685
26691
|
}
|
|
@@ -27010,21 +27016,45 @@ var init_peer_registry = __esm({
|
|
|
27010
27016
|
}
|
|
27011
27017
|
});
|
|
27012
27018
|
|
|
27019
|
+
// src/services/agent-config.ts
|
|
27020
|
+
import os4 from "node:os";
|
|
27021
|
+
import path8 from "node:path";
|
|
27022
|
+
function claudeConfigDir() {
|
|
27023
|
+
const override = process.env.CLAUDE_CONFIG_DIR?.trim();
|
|
27024
|
+
return override ? override : path8.join(os4.homedir(), ".claude");
|
|
27025
|
+
}
|
|
27026
|
+
function codexHomeDir() {
|
|
27027
|
+
const override = process.env.CODEX_HOME?.trim();
|
|
27028
|
+
return override ? override : path8.join(os4.homedir(), ".codex");
|
|
27029
|
+
}
|
|
27030
|
+
function buildResumeEnv(source, vendorPrefix, preserve) {
|
|
27031
|
+
const keep = new Set(preserve);
|
|
27032
|
+
return Object.fromEntries(
|
|
27033
|
+
Object.entries(source).filter(
|
|
27034
|
+
([key]) => (!key.startsWith(vendorPrefix) || keep.has(key)) && key !== "MCP_TRANSPORT"
|
|
27035
|
+
)
|
|
27036
|
+
);
|
|
27037
|
+
}
|
|
27038
|
+
var init_agent_config = __esm({
|
|
27039
|
+
"src/services/agent-config.ts"() {
|
|
27040
|
+
"use strict";
|
|
27041
|
+
}
|
|
27042
|
+
});
|
|
27043
|
+
|
|
27013
27044
|
// src/services/cc-cli.ts
|
|
27014
27045
|
import { spawn } from "node:child_process";
|
|
27015
27046
|
import fs7 from "node:fs/promises";
|
|
27016
|
-
import
|
|
27017
|
-
import path8 from "node:path";
|
|
27047
|
+
import path9 from "node:path";
|
|
27018
27048
|
async function listCandidates(cwd) {
|
|
27019
27049
|
try {
|
|
27020
27050
|
const escapedCwd = cwd.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
27021
|
-
const projectDir =
|
|
27051
|
+
const projectDir = path9.join(claudeConfigDir(), "projects", escapedCwd);
|
|
27022
27052
|
const entries = await fs7.readdir(projectDir);
|
|
27023
27053
|
const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl"));
|
|
27024
27054
|
return await Promise.all(
|
|
27025
27055
|
jsonlFiles.map(async (f) => ({
|
|
27026
27056
|
sessionId: f.replace(".jsonl", ""),
|
|
27027
|
-
mtimeMs: (await fs7.stat(
|
|
27057
|
+
mtimeMs: (await fs7.stat(path9.join(projectDir, f))).mtimeMs
|
|
27028
27058
|
}))
|
|
27029
27059
|
);
|
|
27030
27060
|
} catch {
|
|
@@ -27039,9 +27069,8 @@ async function detectCurrentSession(cwd) {
|
|
|
27039
27069
|
}
|
|
27040
27070
|
async function validateSession(sessionId, cwd) {
|
|
27041
27071
|
const projectHash = cwd.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
27042
|
-
const sessionPath =
|
|
27043
|
-
|
|
27044
|
-
".claude",
|
|
27072
|
+
const sessionPath = path9.join(
|
|
27073
|
+
claudeConfigDir(),
|
|
27045
27074
|
"projects",
|
|
27046
27075
|
projectHash,
|
|
27047
27076
|
`${sessionId}.jsonl`
|
|
@@ -27079,11 +27108,7 @@ function execClaude(sessionId, message, cwd, timeoutMs) {
|
|
|
27079
27108
|
"-p",
|
|
27080
27109
|
truncated
|
|
27081
27110
|
];
|
|
27082
|
-
const cleanEnv =
|
|
27083
|
-
Object.entries(process.env).filter(
|
|
27084
|
-
([key]) => !key.startsWith("CLAUDE") && key !== "MCP_TRANSPORT"
|
|
27085
|
-
)
|
|
27086
|
-
);
|
|
27111
|
+
const cleanEnv = buildResumeEnv(process.env, "CLAUDE", ["CLAUDE_CONFIG_DIR"]);
|
|
27087
27112
|
cleanEnv.COGENT_CHECK_ON_STOP = "0";
|
|
27088
27113
|
const child = spawn(config2.COGENT_CLAUDE_PATH, args, {
|
|
27089
27114
|
cwd,
|
|
@@ -27208,6 +27233,7 @@ var init_cc_cli = __esm({
|
|
|
27208
27233
|
"src/services/cc-cli.ts"() {
|
|
27209
27234
|
"use strict";
|
|
27210
27235
|
init_config();
|
|
27236
|
+
init_agent_config();
|
|
27211
27237
|
}
|
|
27212
27238
|
});
|
|
27213
27239
|
|
|
@@ -27339,14 +27365,14 @@ var init_codex_preflight = __esm({
|
|
|
27339
27365
|
|
|
27340
27366
|
// src/services/health-check.ts
|
|
27341
27367
|
import fs9 from "node:fs/promises";
|
|
27342
|
-
import
|
|
27368
|
+
import path10 from "node:path";
|
|
27343
27369
|
import { execFile as execFile3 } from "node:child_process";
|
|
27344
27370
|
function getStatePath2() {
|
|
27345
|
-
return
|
|
27371
|
+
return path10.join(getConfig().COGENT_STATE_PATH, "cogent-state.json");
|
|
27346
27372
|
}
|
|
27347
27373
|
async function checkStateFile() {
|
|
27348
27374
|
const statePath = getStatePath2();
|
|
27349
|
-
const dir =
|
|
27375
|
+
const dir = path10.dirname(statePath);
|
|
27350
27376
|
try {
|
|
27351
27377
|
await fs9.access(dir, fs9.constants.R_OK | fs9.constants.W_OK);
|
|
27352
27378
|
} catch {
|
|
@@ -27424,7 +27450,7 @@ async function checkHealth() {
|
|
|
27424
27450
|
return {
|
|
27425
27451
|
healthy,
|
|
27426
27452
|
mcpServerVersion: SERVER_VERSION,
|
|
27427
|
-
statePath:
|
|
27453
|
+
statePath: path10.join(config2.COGENT_STATE_PATH, "cogent-state.json"),
|
|
27428
27454
|
claudePath: config2.COGENT_CLAUDE_PATH,
|
|
27429
27455
|
checks,
|
|
27430
27456
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -27463,8 +27489,8 @@ var init_file_backend = __esm({
|
|
|
27463
27489
|
threadId
|
|
27464
27490
|
);
|
|
27465
27491
|
}
|
|
27466
|
-
deregisterPeer(peerId) {
|
|
27467
|
-
return deregisterPeer(peerId);
|
|
27492
|
+
async deregisterPeer(peerId) {
|
|
27493
|
+
return { removed: await deregisterPeer(peerId), mailboxDeprovisioned: false };
|
|
27468
27494
|
}
|
|
27469
27495
|
getPeer(peerId) {
|
|
27470
27496
|
return getPeer(peerId);
|
|
@@ -27531,7 +27557,7 @@ var init_relay_version_cache = __esm({
|
|
|
27531
27557
|
});
|
|
27532
27558
|
|
|
27533
27559
|
// src/backend/http-backend.ts
|
|
27534
|
-
var PATHS, HttpBackend;
|
|
27560
|
+
var MAILBOX_DEPROVISIONED_HEADER, PATHS, HttpBackend;
|
|
27535
27561
|
var init_http_backend = __esm({
|
|
27536
27562
|
"src/backend/http-backend.ts"() {
|
|
27537
27563
|
"use strict";
|
|
@@ -27539,6 +27565,7 @@ var init_http_backend = __esm({
|
|
|
27539
27565
|
init_constants();
|
|
27540
27566
|
init_config();
|
|
27541
27567
|
init_relay_version_cache();
|
|
27568
|
+
MAILBOX_DEPROVISIONED_HEADER = "X-Cogent-Mailbox-Deprovisioned";
|
|
27542
27569
|
PATHS = {
|
|
27543
27570
|
peers: "/api/sessions/:sessionId/peers",
|
|
27544
27571
|
peer: "/api/sessions/:sessionId/peers/:peerId",
|
|
@@ -27562,7 +27589,7 @@ var init_http_backend = __esm({
|
|
|
27562
27589
|
* in cloud mode we ignore it and use this.sessionId (the cloud session).
|
|
27563
27590
|
*/
|
|
27564
27591
|
async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
|
|
27565
|
-
const
|
|
27592
|
+
const path17 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27566
27593
|
const body = {
|
|
27567
27594
|
peerId,
|
|
27568
27595
|
cwd,
|
|
@@ -27590,21 +27617,25 @@ var init_http_backend = __esm({
|
|
|
27590
27617
|
body.role = roleCap.slice("role:".length);
|
|
27591
27618
|
}
|
|
27592
27619
|
}
|
|
27593
|
-
return this.http.post(
|
|
27620
|
+
return this.http.post(path17, body);
|
|
27594
27621
|
}
|
|
27595
27622
|
/**
|
|
27596
27623
|
* Deregister a peer from the cloud session.
|
|
27597
27624
|
* DELETE /api/sessions/:sessionId/peers/:peerId
|
|
27598
27625
|
*
|
|
27599
|
-
* Returns
|
|
27626
|
+
* Returns `{ removed, mailboxDeprovisioned }`. `mailboxDeprovisioned` reflects the relay's
|
|
27627
|
+
* `X-Cogent-Mailbox-Deprovisioned` response header (Cogent Mail F3) — true ONLY when the relay
|
|
27628
|
+
* confirmed the Team mailbox was reaped, so the deregister-peer tool can clear the now-dead
|
|
27629
|
+
* local mail creds. A 404 (peer not found) resolves to `{ removed: false, ... }`, not a throw.
|
|
27600
27630
|
*/
|
|
27601
27631
|
async deregisterPeer(peerId) {
|
|
27602
|
-
const
|
|
27632
|
+
const path17 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
|
|
27603
27633
|
try {
|
|
27604
|
-
await this.http.delete(
|
|
27605
|
-
|
|
27634
|
+
const headers = await this.http.delete(path17, {});
|
|
27635
|
+
const mailboxDeprovisioned = headers.get(MAILBOX_DEPROVISIONED_HEADER) === "true";
|
|
27636
|
+
return { removed: true, mailboxDeprovisioned };
|
|
27606
27637
|
} catch {
|
|
27607
|
-
return false;
|
|
27638
|
+
return { removed: false, mailboxDeprovisioned: false };
|
|
27608
27639
|
}
|
|
27609
27640
|
}
|
|
27610
27641
|
/**
|
|
@@ -27621,8 +27652,8 @@ var init_http_backend = __esm({
|
|
|
27621
27652
|
* GET /api/sessions/:sessionId/peers
|
|
27622
27653
|
*/
|
|
27623
27654
|
async listPeers() {
|
|
27624
|
-
const
|
|
27625
|
-
const result = await this.http.get(
|
|
27655
|
+
const path17 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27656
|
+
const result = await this.http.get(path17);
|
|
27626
27657
|
return result.peers;
|
|
27627
27658
|
}
|
|
27628
27659
|
/**
|
|
@@ -27630,8 +27661,8 @@ var init_http_backend = __esm({
|
|
|
27630
27661
|
* Calls the lightweight heartbeat endpoint on the cloud relay server.
|
|
27631
27662
|
*/
|
|
27632
27663
|
async updateLastSeen(peerId) {
|
|
27633
|
-
const
|
|
27634
|
-
await this.http.post(
|
|
27664
|
+
const path17 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
|
|
27665
|
+
await this.http.post(path17, { peerId });
|
|
27635
27666
|
}
|
|
27636
27667
|
/**
|
|
27637
27668
|
* Record a message in the cloud session.
|
|
@@ -27642,7 +27673,7 @@ var init_http_backend = __esm({
|
|
|
27642
27673
|
* the input record to construct a full MessageRecord.
|
|
27643
27674
|
*/
|
|
27644
27675
|
async recordMessage(record2) {
|
|
27645
|
-
const
|
|
27676
|
+
const path17 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27646
27677
|
const body = {
|
|
27647
27678
|
fromPeerId: record2.fromPeerId,
|
|
27648
27679
|
toPeerId: record2.toPeerId,
|
|
@@ -27651,7 +27682,10 @@ var init_http_backend = __esm({
|
|
|
27651
27682
|
if (record2.isRelayEcho === true) {
|
|
27652
27683
|
body.isRelayEcho = true;
|
|
27653
27684
|
}
|
|
27654
|
-
|
|
27685
|
+
if (record2.attachments && record2.attachments.length > 0) {
|
|
27686
|
+
body.attachments = record2.attachments;
|
|
27687
|
+
}
|
|
27688
|
+
const resp = await this.http.post(path17, body);
|
|
27655
27689
|
return {
|
|
27656
27690
|
...record2,
|
|
27657
27691
|
id: resp.id,
|
|
@@ -27665,7 +27699,7 @@ var init_http_backend = __esm({
|
|
|
27665
27699
|
* Supports optional peerId filter and limit (default 300).
|
|
27666
27700
|
*/
|
|
27667
27701
|
async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
|
|
27668
|
-
const
|
|
27702
|
+
const path17 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27669
27703
|
const query = {};
|
|
27670
27704
|
if (peerId) {
|
|
27671
27705
|
query.peerId = peerId;
|
|
@@ -27680,7 +27714,7 @@ var init_http_backend = __esm({
|
|
|
27680
27714
|
if (dedupInferredEchoes) {
|
|
27681
27715
|
query.dedupInferredEchoes = "true";
|
|
27682
27716
|
}
|
|
27683
|
-
const result = await this.http.get(
|
|
27717
|
+
const result = await this.http.get(path17, query);
|
|
27684
27718
|
return result.messages;
|
|
27685
27719
|
}
|
|
27686
27720
|
/**
|
|
@@ -27691,12 +27725,12 @@ var init_http_backend = __esm({
|
|
|
27691
27725
|
* so that get-history shows the actual response (not null).
|
|
27692
27726
|
*/
|
|
27693
27727
|
async updateMessageResponse(messageId, response, durationMs, pending) {
|
|
27694
|
-
const
|
|
27728
|
+
const path17 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
|
|
27695
27729
|
const body = { response, durationMs };
|
|
27696
27730
|
if (pending === true) {
|
|
27697
27731
|
body.pending = true;
|
|
27698
27732
|
}
|
|
27699
|
-
await this.http.patch(
|
|
27733
|
+
await this.http.patch(path17, body);
|
|
27700
27734
|
}
|
|
27701
27735
|
/**
|
|
27702
27736
|
* Validate a session.
|
|
@@ -27838,8 +27872,8 @@ var init_http_client = __esm({
|
|
|
27838
27872
|
* Perform an authenticated GET request.
|
|
27839
27873
|
* Appends query parameters to the URL if provided.
|
|
27840
27874
|
*/
|
|
27841
|
-
async get(
|
|
27842
|
-
const url = new URL(
|
|
27875
|
+
async get(path17, query) {
|
|
27876
|
+
const url = new URL(path17, this.baseUrl);
|
|
27843
27877
|
if (query) {
|
|
27844
27878
|
for (const [k, v] of Object.entries(query)) {
|
|
27845
27879
|
url.searchParams.set(k, v);
|
|
@@ -27854,8 +27888,8 @@ var init_http_client = __esm({
|
|
|
27854
27888
|
/**
|
|
27855
27889
|
* Perform an authenticated POST request with a JSON body.
|
|
27856
27890
|
*/
|
|
27857
|
-
async post(
|
|
27858
|
-
const url = new URL(
|
|
27891
|
+
async post(path17, body) {
|
|
27892
|
+
const url = new URL(path17, this.baseUrl);
|
|
27859
27893
|
const resp = await this.fetchWithTimeout(url, {
|
|
27860
27894
|
method: "POST",
|
|
27861
27895
|
headers: this.headers(),
|
|
@@ -27873,8 +27907,8 @@ var init_http_client = __esm({
|
|
|
27873
27907
|
* Perform an authenticated PATCH request with a JSON body.
|
|
27874
27908
|
* Used to update existing resources (e.g., message response fields).
|
|
27875
27909
|
*/
|
|
27876
|
-
async patch(
|
|
27877
|
-
const url = new URL(
|
|
27910
|
+
async patch(path17, body) {
|
|
27911
|
+
const url = new URL(path17, this.baseUrl);
|
|
27878
27912
|
const resp = await this.fetchWithTimeout(url, {
|
|
27879
27913
|
method: "PATCH",
|
|
27880
27914
|
headers: this.headers(),
|
|
@@ -27890,15 +27924,17 @@ var init_http_client = __esm({
|
|
|
27890
27924
|
}
|
|
27891
27925
|
/**
|
|
27892
27926
|
* Perform an authenticated DELETE request.
|
|
27893
|
-
* Returns
|
|
27927
|
+
* Returns the RESPONSE HEADERS (a 204 carries no body, but the relay conveys
|
|
27928
|
+
* out-of-band signals — e.g. Cogent Mail F3's `X-Cogent-Mailbox-Deprovisioned` —
|
|
27929
|
+
* via headers, so callers that need them can read them without a body).
|
|
27894
27930
|
*
|
|
27895
27931
|
* When no body is provided, Content-Type is omitted so servers don't try
|
|
27896
27932
|
* to parse an empty payload as JSON. This was the root cause of a
|
|
27897
27933
|
* "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
|
|
27898
27934
|
* any client (including third-party tools) DELETEd a peer without a body.
|
|
27899
27935
|
*/
|
|
27900
|
-
async delete(
|
|
27901
|
-
const url = new URL(
|
|
27936
|
+
async delete(path17, body) {
|
|
27937
|
+
const url = new URL(path17, this.baseUrl);
|
|
27902
27938
|
const headers = {
|
|
27903
27939
|
"Authorization": `Bearer ${this.token}`
|
|
27904
27940
|
};
|
|
@@ -27913,6 +27949,7 @@ var init_http_client = __esm({
|
|
|
27913
27949
|
if (!resp.ok && resp.status !== 204) {
|
|
27914
27950
|
await this.throwMappedError(resp);
|
|
27915
27951
|
}
|
|
27952
|
+
return resp.headers;
|
|
27916
27953
|
}
|
|
27917
27954
|
/**
|
|
27918
27955
|
* Update the Bearer token (e.g., after join-session returns a new token).
|
|
@@ -32077,14 +32114,14 @@ var init_ws_client = __esm({
|
|
|
32077
32114
|
if (this.pollTimer) return;
|
|
32078
32115
|
const poll = async () => {
|
|
32079
32116
|
try {
|
|
32080
|
-
const
|
|
32117
|
+
const path17 = `/api/sessions/${this.opts.sessionId}/poll`;
|
|
32081
32118
|
const query = {
|
|
32082
32119
|
peerId: this.opts.peerId
|
|
32083
32120
|
};
|
|
32084
32121
|
if (this.lastMessageId) {
|
|
32085
32122
|
query.lastMessageId = this.lastMessageId;
|
|
32086
32123
|
}
|
|
32087
|
-
const result = await this.opts.http.get(
|
|
32124
|
+
const result = await this.opts.http.get(path17, query);
|
|
32088
32125
|
if (result.messages && result.messages.length > 0) {
|
|
32089
32126
|
this.opts.onMessages(result.messages);
|
|
32090
32127
|
this.lastMessageId = result.messages[result.messages.length - 1].id;
|
|
@@ -32143,7 +32180,7 @@ var init_cloud = __esm({
|
|
|
32143
32180
|
import { spawn as spawn2 } from "node:child_process";
|
|
32144
32181
|
import fs10 from "node:fs/promises";
|
|
32145
32182
|
import os5 from "node:os";
|
|
32146
|
-
import
|
|
32183
|
+
import path11 from "node:path";
|
|
32147
32184
|
import { randomUUID } from "node:crypto";
|
|
32148
32185
|
import { createReadStream } from "node:fs";
|
|
32149
32186
|
import { createInterface as createInterface2 } from "node:readline";
|
|
@@ -32171,7 +32208,7 @@ function execCodex(sessionId, message, cwd, timeoutMs) {
|
|
|
32171
32208
|
const config2 = getConfig();
|
|
32172
32209
|
const effectiveTimeout = timeoutMs ?? config2.COGENT_TIMEOUT_MS;
|
|
32173
32210
|
const truncated = config2.COGENT_CHAR_LIMIT > 0 && message.length > config2.COGENT_CHAR_LIMIT ? message.slice(0, config2.COGENT_CHAR_LIMIT) + "\n...[truncated]" : message;
|
|
32174
|
-
const lastMsgFile =
|
|
32211
|
+
const lastMsgFile = path11.join(os5.tmpdir(), `cogent-codex-${randomUUID()}.txt`);
|
|
32175
32212
|
const args = [
|
|
32176
32213
|
"exec",
|
|
32177
32214
|
"resume",
|
|
@@ -32182,11 +32219,7 @@ function execCodex(sessionId, message, cwd, timeoutMs) {
|
|
|
32182
32219
|
lastMsgFile,
|
|
32183
32220
|
truncated
|
|
32184
32221
|
];
|
|
32185
|
-
const cleanEnv =
|
|
32186
|
-
Object.entries(process.env).filter(
|
|
32187
|
-
([key]) => !key.startsWith("CODEX") && key !== "MCP_TRANSPORT"
|
|
32188
|
-
)
|
|
32189
|
-
);
|
|
32222
|
+
const cleanEnv = buildResumeEnv(process.env, "CODEX", ["CODEX_HOME"]);
|
|
32190
32223
|
cleanEnv.COGENT_CHECK_ON_STOP = "0";
|
|
32191
32224
|
const child = spawn2(config2.COGENT_CODEX_PATH, args, {
|
|
32192
32225
|
cwd,
|
|
@@ -32278,7 +32311,7 @@ async function listRolloutFiles(root) {
|
|
|
32278
32311
|
return;
|
|
32279
32312
|
}
|
|
32280
32313
|
for (const e of entries) {
|
|
32281
|
-
const full =
|
|
32314
|
+
const full = path11.join(dir, e.name);
|
|
32282
32315
|
if (e.isDirectory()) await walk(full);
|
|
32283
32316
|
else if (e.isFile() && e.name.endsWith(".jsonl")) out.push(full);
|
|
32284
32317
|
}
|
|
@@ -32310,10 +32343,10 @@ async function readSessionMeta(file) {
|
|
|
32310
32343
|
}
|
|
32311
32344
|
}
|
|
32312
32345
|
function codexSessionsRoot() {
|
|
32313
|
-
return
|
|
32346
|
+
return path11.join(codexHomeDir(), "sessions");
|
|
32314
32347
|
}
|
|
32315
32348
|
async function cwdCanonicals(cwd) {
|
|
32316
|
-
const resolved =
|
|
32349
|
+
const resolved = path11.resolve(cwd);
|
|
32317
32350
|
const forms = /* @__PURE__ */ new Set([resolved]);
|
|
32318
32351
|
try {
|
|
32319
32352
|
forms.add(await fs10.realpath(resolved));
|
|
@@ -32322,7 +32355,7 @@ async function cwdCanonicals(cwd) {
|
|
|
32322
32355
|
return forms;
|
|
32323
32356
|
}
|
|
32324
32357
|
function metaCwdMatches(metaCwd, forms) {
|
|
32325
|
-
return forms.has(
|
|
32358
|
+
return forms.has(path11.resolve(metaCwd));
|
|
32326
32359
|
}
|
|
32327
32360
|
async function detectCurrentCodexSession(cwd) {
|
|
32328
32361
|
try {
|
|
@@ -32381,6 +32414,7 @@ var init_codex_cli = __esm({
|
|
|
32381
32414
|
"src/services/codex-cli.ts"() {
|
|
32382
32415
|
"use strict";
|
|
32383
32416
|
init_config();
|
|
32417
|
+
init_agent_config();
|
|
32384
32418
|
sandboxOverride = null;
|
|
32385
32419
|
SANDBOX_FAILURE_RE = /bwrap:|setting up uid ?map|\/proc\/self\/(uid|gid)_map|newuidmap|user namespaces?|RTM_NEWADDR|unshare[^\n]*(Operation not permitted|Permission denied)|clone3?[^\n]*(EPERM|not permitted)/i;
|
|
32386
32420
|
}
|
|
@@ -32514,27 +32548,40 @@ function parseStatusNotice(message) {
|
|
|
32514
32548
|
const m = STATUS_MARKER_RE.exec(message);
|
|
32515
32549
|
return m ? m[1] : null;
|
|
32516
32550
|
}
|
|
32517
|
-
function
|
|
32551
|
+
function formatAttachmentsBlock(attachments) {
|
|
32552
|
+
if (!attachments || attachments.length === 0) return "";
|
|
32553
|
+
const lines = attachments.map((a) => {
|
|
32554
|
+
const name = a.name || a.mailMessageId || a.url || "file";
|
|
32555
|
+
const ref = a.mailMessageId ? `fetch via cogent_fetch_mail (${a.mailMessageId})` : a.url ? a.url : "no fetchable reference";
|
|
32556
|
+
const meta = [a.mimeType, a.size != null ? `${a.size}B` : null].filter(Boolean).join(", ");
|
|
32557
|
+
return ` [file: ${name}${meta ? ` (${meta})` : ""} \u2014 ${ref}]`;
|
|
32558
|
+
});
|
|
32559
|
+
return `
|
|
32560
|
+
|
|
32561
|
+
\u{1F4CE} Attachments (byte transport = email):
|
|
32562
|
+
${lines.join("\n")}`;
|
|
32563
|
+
}
|
|
32564
|
+
function formatRelayMessage(fromLabel, fromPeerId, message, attachments) {
|
|
32518
32565
|
return `[Cogent Bridge message from ${fromLabel} (${fromPeerId})]
|
|
32519
32566
|
|
|
32520
|
-
${message}
|
|
32567
|
+
${message}${formatAttachmentsBlock(attachments)}
|
|
32521
32568
|
|
|
32522
32569
|
---
|
|
32523
32570
|
Respond directly to the message above. Your entire response is delivered automatically to whoever should receive it \u2014 a broadcast question (to the whole channel) is answered to everyone; a direct message, to the sender. You do NOT need to \u2014 and should NOT \u2014 call cogent_send_message or any bridge tool to deliver it; just answer normally.`;
|
|
32524
32571
|
}
|
|
32525
|
-
function formatInjectOnlyMessage(fromLabel, fromPeerId, message) {
|
|
32572
|
+
function formatInjectOnlyMessage(fromLabel, fromPeerId, message, attachments) {
|
|
32526
32573
|
return `[Cogent Bridge: Response from ${fromLabel} (${fromPeerId})]
|
|
32527
32574
|
|
|
32528
|
-
${message}
|
|
32575
|
+
${message}${formatAttachmentsBlock(attachments)}
|
|
32529
32576
|
|
|
32530
32577
|
---
|
|
32531
32578
|
This is the response to your earlier cogent_send_message. No reply will be relayed.
|
|
32532
32579
|
To continue the conversation, use cogent_send_message again.`;
|
|
32533
32580
|
}
|
|
32534
|
-
function formatPeerBroadcastNotice(fromLabel, fromPeerId, message) {
|
|
32581
|
+
function formatPeerBroadcastNotice(fromLabel, fromPeerId, message, attachments) {
|
|
32535
32582
|
return `[Cogent Bridge: broadcast from ${fromLabel} (${fromPeerId}) to the whole channel]
|
|
32536
32583
|
|
|
32537
|
-
${message}
|
|
32584
|
+
${message}${formatAttachmentsBlock(attachments)}
|
|
32538
32585
|
|
|
32539
32586
|
---
|
|
32540
32587
|
FYI ONLY \u2014 another agent addressed the whole channel. Take note for context, but do NOT reply and do NOT call any cogent_* tool in response. A human will drive any reply.`;
|
|
@@ -33076,7 +33123,7 @@ var init_auto_relay = __esm({
|
|
|
33076
33123
|
}
|
|
33077
33124
|
} catch {
|
|
33078
33125
|
}
|
|
33079
|
-
const formatted = formatRelayMessage(fromLabel, msg.fromPeerId, msg.message);
|
|
33126
|
+
const formatted = formatRelayMessage(fromLabel, msg.fromPeerId, msg.message, msg.attachments);
|
|
33080
33127
|
logger.info(`Auto-relay: processing message from ${fromLabel} (${msg.fromPeerId})`);
|
|
33081
33128
|
const traceId = msg.traceId ?? randomUUID2();
|
|
33082
33129
|
const startMs = Date.now();
|
|
@@ -33234,7 +33281,7 @@ var init_auto_relay = __esm({
|
|
|
33234
33281
|
}
|
|
33235
33282
|
} catch {
|
|
33236
33283
|
}
|
|
33237
|
-
const formatted = formatInjectOnlyMessage(fromLabel, msg.fromPeerId, msg.message);
|
|
33284
|
+
const formatted = formatInjectOnlyMessage(fromLabel, msg.fromPeerId, msg.message, msg.attachments);
|
|
33238
33285
|
try {
|
|
33239
33286
|
await this.refreshSessionId();
|
|
33240
33287
|
const result = await execRemote(
|
|
@@ -33269,7 +33316,7 @@ var init_auto_relay = __esm({
|
|
|
33269
33316
|
}
|
|
33270
33317
|
} catch {
|
|
33271
33318
|
}
|
|
33272
|
-
const formatted = formatPeerBroadcastNotice(fromLabel, msg.fromPeerId, msg.message);
|
|
33319
|
+
const formatted = formatPeerBroadcastNotice(fromLabel, msg.fromPeerId, msg.message, msg.attachments);
|
|
33273
33320
|
try {
|
|
33274
33321
|
await this.refreshSessionId();
|
|
33275
33322
|
const result = await execRemote(
|
|
@@ -33573,7 +33620,7 @@ __export(startup_exports, {
|
|
|
33573
33620
|
triggerReRegistration: () => triggerReRegistration
|
|
33574
33621
|
});
|
|
33575
33622
|
import os6 from "node:os";
|
|
33576
|
-
import
|
|
33623
|
+
import path12 from "node:path";
|
|
33577
33624
|
import fs11 from "node:fs/promises";
|
|
33578
33625
|
import readline2 from "node:readline";
|
|
33579
33626
|
import { execFile as execFile5 } from "node:child_process";
|
|
@@ -33624,7 +33671,7 @@ First run detected. Where should cogent-bridge store its data?
|
|
|
33624
33671
|
}
|
|
33625
33672
|
async function validateStateDir(statePath) {
|
|
33626
33673
|
await fs11.mkdir(statePath, { recursive: true });
|
|
33627
|
-
const testFile =
|
|
33674
|
+
const testFile = path12.join(statePath, ".write-test");
|
|
33628
33675
|
try {
|
|
33629
33676
|
await fs11.writeFile(testFile, "test", "utf-8");
|
|
33630
33677
|
await fs11.unlink(testFile);
|
|
@@ -33773,10 +33820,10 @@ async function runStartup() {
|
|
|
33773
33820
|
if (envStatePath) {
|
|
33774
33821
|
statePath = envStatePath;
|
|
33775
33822
|
} else {
|
|
33776
|
-
const defaultPath =
|
|
33823
|
+
const defaultPath = path12.join(os6.homedir(), ".cogent");
|
|
33777
33824
|
statePath = await firstRunPrompt(defaultPath);
|
|
33778
33825
|
}
|
|
33779
|
-
if (!envStatePath && statePath !==
|
|
33826
|
+
if (!envStatePath && statePath !== path12.join(os6.homedir(), ".cogent")) {
|
|
33780
33827
|
process.env.COGENT_STATE_PATH = statePath;
|
|
33781
33828
|
} else if (!envStatePath) {
|
|
33782
33829
|
process.env.COGENT_STATE_PATH = statePath;
|
|
@@ -33853,7 +33900,7 @@ async function runStartup() {
|
|
|
33853
33900
|
}
|
|
33854
33901
|
initLogger(
|
|
33855
33902
|
config2.COGENT_LOG_LEVEL,
|
|
33856
|
-
|
|
33903
|
+
path12.join(config2.COGENT_STATE_PATH, "logs")
|
|
33857
33904
|
);
|
|
33858
33905
|
if (isCloud && effectiveSessionId && effectiveToken) {
|
|
33859
33906
|
cloudInbox = new MessageInbox();
|
|
@@ -33910,7 +33957,7 @@ var init_startup = __esm({
|
|
|
33910
33957
|
init_auto_relay();
|
|
33911
33958
|
UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
33912
33959
|
execFileAsync3 = promisify4(execFile5);
|
|
33913
|
-
PERSIST_PATH =
|
|
33960
|
+
PERSIST_PATH = path12.join(os6.homedir(), ".cogent-config.json");
|
|
33914
33961
|
legacyWarnEmitted = false;
|
|
33915
33962
|
cloudWsClient = null;
|
|
33916
33963
|
cloudInbox = null;
|
|
@@ -33922,11 +33969,11 @@ var init_startup = __esm({
|
|
|
33922
33969
|
// src/cloud/mail-credential-store.ts
|
|
33923
33970
|
import crypto3 from "node:crypto";
|
|
33924
33971
|
import fs12 from "node:fs/promises";
|
|
33925
|
-
import
|
|
33972
|
+
import path13 from "node:path";
|
|
33926
33973
|
import os7 from "node:os";
|
|
33927
33974
|
function defaultMailCredentialPath(cwd = process.cwd()) {
|
|
33928
|
-
const hash = crypto3.createHash("sha256").update(
|
|
33929
|
-
return
|
|
33975
|
+
const hash = crypto3.createHash("sha256").update(path13.resolve(cwd)).digest("hex").slice(0, 16);
|
|
33976
|
+
return path13.join(os7.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
|
|
33930
33977
|
}
|
|
33931
33978
|
function resolveMailCredentialPath(credentialPath) {
|
|
33932
33979
|
if (credentialPath) return credentialPath;
|
|
@@ -33944,7 +33991,7 @@ async function loadMailCredentials(credentialPath) {
|
|
|
33944
33991
|
}
|
|
33945
33992
|
async function saveMailCredentials(creds, credentialPath) {
|
|
33946
33993
|
const filePath = resolveMailCredentialPath(credentialPath);
|
|
33947
|
-
await fs12.mkdir(
|
|
33994
|
+
await fs12.mkdir(path13.dirname(filePath), { recursive: true, mode: 448 });
|
|
33948
33995
|
await fs12.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
33949
33996
|
await fs12.chmod(filePath, 384);
|
|
33950
33997
|
}
|
|
@@ -33964,6 +34011,14 @@ async function persistProvisionedMailbox(mailbox, credentialPath) {
|
|
|
33964
34011
|
);
|
|
33965
34012
|
return true;
|
|
33966
34013
|
}
|
|
34014
|
+
async function clearMailCredentials(credentialPath) {
|
|
34015
|
+
const filePath = resolveMailCredentialPath(credentialPath);
|
|
34016
|
+
try {
|
|
34017
|
+
await fs12.unlink(filePath);
|
|
34018
|
+
} catch (err) {
|
|
34019
|
+
if (err.code !== "ENOENT") throw err;
|
|
34020
|
+
}
|
|
34021
|
+
}
|
|
33967
34022
|
var MAIL_DEFAULT_HOST;
|
|
33968
34023
|
var init_mail_credential_store = __esm({
|
|
33969
34024
|
"src/cloud/mail-credential-store.ts"() {
|
|
@@ -34519,11 +34574,18 @@ var init_peer = __esm({
|
|
|
34519
34574
|
});
|
|
34520
34575
|
|
|
34521
34576
|
// cogent/dist/types/message.js
|
|
34522
|
-
var import_zod6, MessageRecordSchema;
|
|
34577
|
+
var import_zod6, AttachmentSchema, MessageRecordSchema;
|
|
34523
34578
|
var init_message = __esm({
|
|
34524
34579
|
"cogent/dist/types/message.js"() {
|
|
34525
34580
|
"use strict";
|
|
34526
34581
|
import_zod6 = __toESM(require_zod(), 1);
|
|
34582
|
+
AttachmentSchema = import_zod6.z.object({
|
|
34583
|
+
url: import_zod6.z.string().optional().describe("Public/fetchable URL reference"),
|
|
34584
|
+
mailMessageId: import_zod6.z.string().optional().describe("IMAP message-id in the recipient mailbox"),
|
|
34585
|
+
name: import_zod6.z.string().optional().describe("Display name / filename"),
|
|
34586
|
+
mimeType: import_zod6.z.string().optional().describe("MIME type"),
|
|
34587
|
+
size: import_zod6.z.number().int().nonnegative().optional().describe("Size in bytes")
|
|
34588
|
+
}).describe("A file reference carried alongside a message (byte transport = email)");
|
|
34527
34589
|
MessageRecordSchema = import_zod6.z.object({
|
|
34528
34590
|
id: import_zod6.z.string().uuid().describe("Unique message identifier"),
|
|
34529
34591
|
fromPeerId: import_zod6.z.string().min(1).describe("Sender peer ID"),
|
|
@@ -34534,7 +34596,8 @@ var init_message = __esm({
|
|
|
34534
34596
|
durationMs: import_zod6.z.number().nullable().describe("Round-trip duration in ms"),
|
|
34535
34597
|
success: import_zod6.z.boolean().describe("Whether delivery succeeded"),
|
|
34536
34598
|
error: import_zod6.z.string().nullable().describe("Error message if delivery failed"),
|
|
34537
|
-
originPlatform: import_zod6.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini", "whatsapp", "telegram", "discord"]).optional().describe("Platform that originated this message")
|
|
34599
|
+
originPlatform: import_zod6.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini", "whatsapp", "telegram", "discord"]).optional().describe("Platform that originated this message"),
|
|
34600
|
+
attachments: import_zod6.z.array(AttachmentSchema).optional().describe("Cogent Mail M2.5 \u2014 file references (byte transport = email)")
|
|
34538
34601
|
});
|
|
34539
34602
|
}
|
|
34540
34603
|
});
|
|
@@ -35094,8 +35157,16 @@ function registerDeregisterPeerTool(server) {
|
|
|
35094
35157
|
async ({ peerId }) => {
|
|
35095
35158
|
try {
|
|
35096
35159
|
const backend = getBackend();
|
|
35097
|
-
const removed = await backend.deregisterPeer(peerId);
|
|
35160
|
+
const { removed, mailboxDeprovisioned } = await backend.deregisterPeer(peerId);
|
|
35098
35161
|
if (removed) heartbeat.stop();
|
|
35162
|
+
if (mailboxDeprovisioned) {
|
|
35163
|
+
try {
|
|
35164
|
+
await clearMailCredentials();
|
|
35165
|
+
logger.info("cleared local mail credentials after mailbox deprovision", { peerId });
|
|
35166
|
+
} catch (err) {
|
|
35167
|
+
logger.warn("failed to clear local mail credentials after deprovision", { error: err });
|
|
35168
|
+
}
|
|
35169
|
+
}
|
|
35099
35170
|
return successResult({
|
|
35100
35171
|
success: removed,
|
|
35101
35172
|
message: removed ? `Peer '${peerId}' deregistered` : `Peer '${peerId}' was not registered`
|
|
@@ -35116,6 +35187,7 @@ var init_deregister_peer = __esm({
|
|
|
35116
35187
|
init_errors4();
|
|
35117
35188
|
init_logger();
|
|
35118
35189
|
init_heartbeat();
|
|
35190
|
+
init_mail_credential_store();
|
|
35119
35191
|
}
|
|
35120
35192
|
});
|
|
35121
35193
|
|
|
@@ -35501,12 +35573,36 @@ function assertValidEmail(value, field) {
|
|
|
35501
35573
|
);
|
|
35502
35574
|
}
|
|
35503
35575
|
}
|
|
35504
|
-
|
|
35576
|
+
function parseAllowedMailDomains(csv) {
|
|
35577
|
+
const list = (csv ?? DEFAULT_MAIL_DOMAIN).split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
35578
|
+
return list.length ? list : [DEFAULT_MAIL_DOMAIN];
|
|
35579
|
+
}
|
|
35580
|
+
function allowedMailDomains() {
|
|
35581
|
+
try {
|
|
35582
|
+
return parseAllowedMailDomains(getConfig().COGENT_MAIL_ALLOWED_DOMAINS);
|
|
35583
|
+
} catch {
|
|
35584
|
+
return parseAllowedMailDomains(process.env.COGENT_MAIL_ALLOWED_DOMAINS);
|
|
35585
|
+
}
|
|
35586
|
+
}
|
|
35587
|
+
function assertAllowedMailDomain(address, field) {
|
|
35588
|
+
const domain = address.split("@")[1]?.toLowerCase() ?? "";
|
|
35589
|
+
const allowed = allowedMailDomains();
|
|
35590
|
+
if (!allowed.includes(domain)) {
|
|
35591
|
+
throw new BridgeError(
|
|
35592
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35593
|
+
`${field} domain "${domain}" is not an allowed Cogent mail domain`,
|
|
35594
|
+
`Cogent mail is limited to: ${allowed.join(", ")}. External or corporate-domain mail is a future release.`
|
|
35595
|
+
);
|
|
35596
|
+
}
|
|
35597
|
+
}
|
|
35598
|
+
var EMAIL_RE, DEFAULT_MAIL_DOMAIN;
|
|
35505
35599
|
var init_validate = __esm({
|
|
35506
35600
|
"src/mail/validate.ts"() {
|
|
35507
35601
|
"use strict";
|
|
35508
35602
|
init_errors4();
|
|
35603
|
+
init_config();
|
|
35509
35604
|
EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
35605
|
+
DEFAULT_MAIL_DOMAIN = "mail.cogent.tools";
|
|
35510
35606
|
}
|
|
35511
35607
|
});
|
|
35512
35608
|
|
|
@@ -35530,6 +35626,7 @@ function registerSetupMailTool(server) {
|
|
|
35530
35626
|
async ({ address, password, imapHost, imapPort, smtpHost, smtpPort }) => {
|
|
35531
35627
|
try {
|
|
35532
35628
|
assertValidEmail(address, "address");
|
|
35629
|
+
assertAllowedMailDomain(address, "address");
|
|
35533
35630
|
await saveMailCredentials({
|
|
35534
35631
|
address,
|
|
35535
35632
|
password,
|
|
@@ -35560,12 +35657,12 @@ var init_setup_mail = __esm({
|
|
|
35560
35657
|
|
|
35561
35658
|
// src/mail/mail-sender.ts
|
|
35562
35659
|
import fs13 from "node:fs/promises";
|
|
35563
|
-
import
|
|
35660
|
+
import path14 from "node:path";
|
|
35564
35661
|
async function buildAndSendMail(sender, creds, params) {
|
|
35565
35662
|
const attachments = [];
|
|
35566
35663
|
for (const p of params.attachmentPaths ?? []) {
|
|
35567
35664
|
const content = await fs13.readFile(p);
|
|
35568
|
-
attachments.push({ filename:
|
|
35665
|
+
attachments.push({ filename: path14.basename(p), content });
|
|
35569
35666
|
}
|
|
35570
35667
|
const { messageId } = await sender.send({
|
|
35571
35668
|
from: creds.address,
|
|
@@ -35613,6 +35710,25 @@ var init_mail_sender = __esm({
|
|
|
35613
35710
|
}
|
|
35614
35711
|
});
|
|
35615
35712
|
|
|
35713
|
+
// src/mail/auth-error.ts
|
|
35714
|
+
function isMailAuthError(err) {
|
|
35715
|
+
if (!err || typeof err !== "object") return false;
|
|
35716
|
+
const e = err;
|
|
35717
|
+
if (e.authenticationFailed === true) return true;
|
|
35718
|
+
if (typeof e.serverResponseCode === "string" && e.serverResponseCode.toUpperCase() === "AUTHENTICATIONFAILED") return true;
|
|
35719
|
+
if (e.code === "EAUTH") return true;
|
|
35720
|
+
if (e.responseCode === 535) return true;
|
|
35721
|
+
return false;
|
|
35722
|
+
}
|
|
35723
|
+
function mailAuthErrorHint() {
|
|
35724
|
+
return "The mail server refused these credentials. The mailbox may be temporarily SUSPENDED by an admin (retry after it is resumed \u2014 your saved credentials will work again), or it was deleted (only then re-run cogent_setup_mail with a fresh address + password). Your local credentials are kept.";
|
|
35725
|
+
}
|
|
35726
|
+
var init_auth_error = __esm({
|
|
35727
|
+
"src/mail/auth-error.ts"() {
|
|
35728
|
+
"use strict";
|
|
35729
|
+
}
|
|
35730
|
+
});
|
|
35731
|
+
|
|
35616
35732
|
// src/tools/send-mail.ts
|
|
35617
35733
|
function registerSendMailTool(server, deps = {}) {
|
|
35618
35734
|
const makeSender = deps.senderFactory ?? ((creds) => new NodemailerMailSender(creds));
|
|
@@ -35632,6 +35748,7 @@ function registerSendMailTool(server, deps = {}) {
|
|
|
35632
35748
|
async ({ to, subject, body, attachments }) => {
|
|
35633
35749
|
try {
|
|
35634
35750
|
assertValidEmail(to, "to");
|
|
35751
|
+
assertAllowedMailDomain(to, "to");
|
|
35635
35752
|
const creds = await loadMailCredentials();
|
|
35636
35753
|
if (!creds) {
|
|
35637
35754
|
return errorResult(
|
|
@@ -35650,6 +35767,10 @@ function registerSendMailTool(server, deps = {}) {
|
|
|
35650
35767
|
});
|
|
35651
35768
|
return successResult({ ...result, attachmentCount: attachments?.length ?? 0 });
|
|
35652
35769
|
} catch (err) {
|
|
35770
|
+
if (isMailAuthError(err)) {
|
|
35771
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
35772
|
+
return errorResult(new BridgeError("INVALID_INPUT" /* INVALID_INPUT */, `Mailbox login was refused: ${detail}`, mailAuthErrorHint()));
|
|
35773
|
+
}
|
|
35653
35774
|
return errorResult(err);
|
|
35654
35775
|
}
|
|
35655
35776
|
}
|
|
@@ -35664,19 +35785,20 @@ var init_send_mail = __esm({
|
|
|
35664
35785
|
init_mail_credential_store();
|
|
35665
35786
|
init_mail_sender();
|
|
35666
35787
|
init_validate();
|
|
35788
|
+
init_auth_error();
|
|
35667
35789
|
}
|
|
35668
35790
|
});
|
|
35669
35791
|
|
|
35670
35792
|
// src/mail/mail-fetcher.ts
|
|
35671
35793
|
import fs14 from "node:fs/promises";
|
|
35672
|
-
import
|
|
35794
|
+
import path15 from "node:path";
|
|
35673
35795
|
async function saveAttachments(attachments, downloadDir) {
|
|
35674
35796
|
if (attachments.length === 0) return [];
|
|
35675
35797
|
await fs14.mkdir(downloadDir, { recursive: true });
|
|
35676
35798
|
const saved = [];
|
|
35677
35799
|
for (const a of attachments) {
|
|
35678
|
-
const name =
|
|
35679
|
-
const savedPath =
|
|
35800
|
+
const name = path15.basename(a.filename) || "attachment";
|
|
35801
|
+
const savedPath = path15.join(downloadDir, name);
|
|
35680
35802
|
await fs14.writeFile(savedPath, a.content);
|
|
35681
35803
|
saved.push({ filename: name, savedPath, size: a.content.length });
|
|
35682
35804
|
}
|
|
@@ -35809,7 +35931,7 @@ var init_mail_fetcher = __esm({
|
|
|
35809
35931
|
|
|
35810
35932
|
// src/tools/fetch-mail.ts
|
|
35811
35933
|
import os8 from "node:os";
|
|
35812
|
-
import
|
|
35934
|
+
import path16 from "node:path";
|
|
35813
35935
|
function registerFetchMailTool(server, deps = {}) {
|
|
35814
35936
|
const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
|
|
35815
35937
|
server.registerTool(
|
|
@@ -35838,6 +35960,7 @@ function registerFetchMailTool(server, deps = {}) {
|
|
|
35838
35960
|
)
|
|
35839
35961
|
);
|
|
35840
35962
|
}
|
|
35963
|
+
assertAllowedMailDomain(creds.address, "mailbox");
|
|
35841
35964
|
const fetcher = makeFetcher(creds);
|
|
35842
35965
|
if (action === "fetch") {
|
|
35843
35966
|
if (uid === void 0) {
|
|
@@ -35851,6 +35974,10 @@ function registerFetchMailTool(server, deps = {}) {
|
|
|
35851
35974
|
const messages = await fetcher.list({ unreadOnly, limit: limit ?? 25 });
|
|
35852
35975
|
return successResult({ mailbox: creds.address, count: messages.length, messages });
|
|
35853
35976
|
} catch (err) {
|
|
35977
|
+
if (isMailAuthError(err)) {
|
|
35978
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
35979
|
+
return errorResult(new BridgeError("INVALID_INPUT" /* INVALID_INPUT */, `Mailbox login was refused: ${detail}`, mailAuthErrorHint()));
|
|
35980
|
+
}
|
|
35854
35981
|
return errorResult(err);
|
|
35855
35982
|
}
|
|
35856
35983
|
}
|
|
@@ -35864,7 +35991,94 @@ var init_fetch_mail = __esm({
|
|
|
35864
35991
|
init_errors4();
|
|
35865
35992
|
init_mail_credential_store();
|
|
35866
35993
|
init_mail_fetcher();
|
|
35867
|
-
|
|
35994
|
+
init_auth_error();
|
|
35995
|
+
init_validate();
|
|
35996
|
+
DEFAULT_DOWNLOAD_DIR = path16.join(os8.homedir(), ".cogent", "mail-downloads");
|
|
35997
|
+
}
|
|
35998
|
+
});
|
|
35999
|
+
|
|
36000
|
+
// src/tools/rotate-mail-creds.ts
|
|
36001
|
+
function registerRotateMailCredsTool(server) {
|
|
36002
|
+
server.registerTool(
|
|
36003
|
+
"cogent_rotate_mail_creds",
|
|
36004
|
+
{
|
|
36005
|
+
title: "Rotate this agent's mailbox password",
|
|
36006
|
+
description: "Rotate (re-key) this agent's Team mailbox password on the mail server and update the local credential store, so cogent_send_mail / cogent_fetch_mail keep working with the fresh secret. Use it if the mailbox password may be compromised. Team channels only.",
|
|
36007
|
+
inputSchema: {},
|
|
36008
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
36009
|
+
},
|
|
36010
|
+
async () => {
|
|
36011
|
+
try {
|
|
36012
|
+
const creds = await loadCredentials();
|
|
36013
|
+
if (!creds || !creds.endpoint || !creds.sessionId || !creds.token) {
|
|
36014
|
+
throw new BridgeError(
|
|
36015
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
36016
|
+
"Not registered in cloud mode",
|
|
36017
|
+
"Rotation applies to a cloud Team channel \u2014 register the peer first"
|
|
36018
|
+
);
|
|
36019
|
+
}
|
|
36020
|
+
if (!creds.peerId) {
|
|
36021
|
+
throw new BridgeError(
|
|
36022
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
36023
|
+
"Cannot identify this peer",
|
|
36024
|
+
"Re-register the peer so its peerId is stored, then rotate"
|
|
36025
|
+
);
|
|
36026
|
+
}
|
|
36027
|
+
const url = `${creds.endpoint.replace(/\/+$/, "")}/api/sessions/${encodeURIComponent(creds.sessionId)}/peers/${encodeURIComponent(creds.peerId)}/mail/rotate`;
|
|
36028
|
+
let res;
|
|
36029
|
+
try {
|
|
36030
|
+
res = await fetch(url, {
|
|
36031
|
+
method: "POST",
|
|
36032
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${creds.token}` },
|
|
36033
|
+
body: "{}",
|
|
36034
|
+
signal: AbortSignal.timeout(ROTATE_TIMEOUT_MS)
|
|
36035
|
+
});
|
|
36036
|
+
} catch {
|
|
36037
|
+
throw new BridgeError("NETWORK_TIMEOUT" /* NETWORK_TIMEOUT */, "Could not reach the relay to rotate the mailbox", "Check connectivity and try again");
|
|
36038
|
+
}
|
|
36039
|
+
if (!res.ok) {
|
|
36040
|
+
const detail = await res.text().catch(() => "");
|
|
36041
|
+
throw new BridgeError(
|
|
36042
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
36043
|
+
`Mailbox rotation was rejected (HTTP ${res.status})`,
|
|
36044
|
+
detail.slice(0, 200) || "Rotation applies to Team channels with a provisioned mailbox"
|
|
36045
|
+
);
|
|
36046
|
+
}
|
|
36047
|
+
const data = await res.json().catch(() => null);
|
|
36048
|
+
if (!data || data.success !== true) {
|
|
36049
|
+
throw new BridgeError("NETWORK_TIMEOUT" /* NETWORK_TIMEOUT */, "Mailbox rotation failed", "The relay returned an unexpected response");
|
|
36050
|
+
}
|
|
36051
|
+
if (!data.rotated) {
|
|
36052
|
+
return successResult({ rotated: false, reason: data.reason ?? "no_mailbox" });
|
|
36053
|
+
}
|
|
36054
|
+
if (!data.address || !data.password) {
|
|
36055
|
+
throw new BridgeError("NETWORK_TIMEOUT" /* NETWORK_TIMEOUT */, "Rotation returned no new password", "Try again; the mailbox password was not updated locally");
|
|
36056
|
+
}
|
|
36057
|
+
const existing = await loadMailCredentials();
|
|
36058
|
+
await saveMailCredentials({
|
|
36059
|
+
address: data.address,
|
|
36060
|
+
password: data.password,
|
|
36061
|
+
imapHost: existing?.imapHost ?? MAIL_DEFAULT_HOST,
|
|
36062
|
+
imapPort: existing?.imapPort ?? 993,
|
|
36063
|
+
smtpHost: existing?.smtpHost ?? MAIL_DEFAULT_HOST,
|
|
36064
|
+
smtpPort: existing?.smtpPort ?? 465,
|
|
36065
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
36066
|
+
});
|
|
36067
|
+
return successResult({ rotated: true, address: data.address });
|
|
36068
|
+
} catch (err) {
|
|
36069
|
+
return errorResult(err);
|
|
36070
|
+
}
|
|
36071
|
+
}
|
|
36072
|
+
);
|
|
36073
|
+
}
|
|
36074
|
+
var ROTATE_TIMEOUT_MS;
|
|
36075
|
+
var init_rotate_mail_creds = __esm({
|
|
36076
|
+
"src/tools/rotate-mail-creds.ts"() {
|
|
36077
|
+
"use strict";
|
|
36078
|
+
init_errors4();
|
|
36079
|
+
init_credential_store();
|
|
36080
|
+
init_mail_credential_store();
|
|
36081
|
+
ROTATE_TIMEOUT_MS = 8e3;
|
|
35868
36082
|
}
|
|
35869
36083
|
});
|
|
35870
36084
|
|
|
@@ -35884,6 +36098,7 @@ async function main() {
|
|
|
35884
36098
|
registerSetupMailTool(server);
|
|
35885
36099
|
registerSendMailTool(server);
|
|
35886
36100
|
registerFetchMailTool(server);
|
|
36101
|
+
registerRotateMailCredsTool(server);
|
|
35887
36102
|
if (cloudInbox) {
|
|
35888
36103
|
server.registerResource(
|
|
35889
36104
|
"cogent_inbox",
|
|
@@ -35946,6 +36161,7 @@ var init_index = __esm({
|
|
|
35946
36161
|
init_setup_mail();
|
|
35947
36162
|
init_send_mail();
|
|
35948
36163
|
init_fetch_mail();
|
|
36164
|
+
init_rotate_mail_creds();
|
|
35949
36165
|
init_heartbeat();
|
|
35950
36166
|
process.on("uncaughtException", (err) => {
|
|
35951
36167
|
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.18.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",
|