@agenticmail/core 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +277 -92
- package/dist/index.d.cts +249 -1
- package/dist/index.d.ts +249 -1
- package/dist/index.js +266 -91
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -726,6 +726,8 @@ __export(index_exports, {
|
|
|
726
726
|
InboxWatcher: () => InboxWatcher,
|
|
727
727
|
MailReceiver: () => MailReceiver,
|
|
728
728
|
MailSender: () => MailSender,
|
|
729
|
+
PathTraversalError: () => PathTraversalError,
|
|
730
|
+
REDACTED: () => REDACTED,
|
|
729
731
|
RELAY_PRESETS: () => RELAY_PRESETS,
|
|
730
732
|
RelayBridge: () => RelayBridge,
|
|
731
733
|
RelayGateway: () => RelayGateway,
|
|
@@ -737,7 +739,10 @@ __export(index_exports, {
|
|
|
737
739
|
StalwartAdmin: () => StalwartAdmin,
|
|
738
740
|
ThreadCache: () => ThreadCache,
|
|
739
741
|
TunnelManager: () => TunnelManager,
|
|
742
|
+
UnsafeApiUrlError: () => UnsafeApiUrlError,
|
|
740
743
|
WARNING_THRESHOLD: () => WARNING_THRESHOLD,
|
|
744
|
+
assertWithinBase: () => assertWithinBase,
|
|
745
|
+
buildApiUrl: () => buildApiUrl,
|
|
741
746
|
buildInboundSecurityAdvisory: () => buildInboundSecurityAdvisory,
|
|
742
747
|
classifyEmailRoute: () => classifyEmailRoute,
|
|
743
748
|
closeDatabase: () => closeDatabase,
|
|
@@ -756,14 +761,19 @@ __export(index_exports, {
|
|
|
756
761
|
parseEmail: () => parseEmail,
|
|
757
762
|
parseGoogleVoiceSms: () => parseGoogleVoiceSms,
|
|
758
763
|
recordToolCall: () => recordToolCall,
|
|
764
|
+
redactObject: () => redactObject,
|
|
765
|
+
redactSecret: () => redactSecret,
|
|
759
766
|
resolveConfig: () => resolveConfig,
|
|
767
|
+
safeJoin: () => safeJoin,
|
|
760
768
|
sanitizeEmail: () => sanitizeEmail,
|
|
761
769
|
saveConfig: () => saveConfig,
|
|
762
770
|
scanOutboundEmail: () => scanOutboundEmail,
|
|
763
771
|
scoreEmail: () => scoreEmail,
|
|
764
772
|
setTelemetryVersion: () => setTelemetryVersion,
|
|
765
773
|
startRelayBridge: () => startRelayBridge,
|
|
766
|
-
threadIdFor: () => threadIdFor
|
|
774
|
+
threadIdFor: () => threadIdFor,
|
|
775
|
+
tryJoin: () => tryJoin,
|
|
776
|
+
validateApiUrl: () => validateApiUrl
|
|
767
777
|
});
|
|
768
778
|
module.exports = __toCommonJS(index_exports);
|
|
769
779
|
|
|
@@ -837,7 +847,7 @@ var MailSender = class {
|
|
|
837
847
|
const code = err?.responseCode ?? err?.code;
|
|
838
848
|
const isTransient = typeof code === "number" && code >= 400 && code < 500 || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ESOCKET";
|
|
839
849
|
if (!isTransient || attempt === MAX_RETRIES) throw err;
|
|
840
|
-
await new Promise((
|
|
850
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3 * (attempt + 1)));
|
|
841
851
|
}
|
|
842
852
|
}
|
|
843
853
|
throw lastError;
|
|
@@ -964,7 +974,13 @@ var MailReceiver = class {
|
|
|
964
974
|
async fetchMessage(uid, mailbox = "INBOX") {
|
|
965
975
|
const lock = await this.client.getMailboxLock(mailbox);
|
|
966
976
|
try {
|
|
967
|
-
const
|
|
977
|
+
const result = await this.client.download(String(uid), void 0, { uid: true });
|
|
978
|
+
const content = result?.content;
|
|
979
|
+
if (!content) {
|
|
980
|
+
const err = new Error(`Message UID ${uid} not found in mailbox "${mailbox}"`);
|
|
981
|
+
err.code = "MESSAGE_NOT_FOUND";
|
|
982
|
+
throw err;
|
|
983
|
+
}
|
|
968
984
|
const chunks = [];
|
|
969
985
|
for await (const chunk of content) {
|
|
970
986
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
@@ -1763,20 +1779,20 @@ var StalwartAdmin = class {
|
|
|
1763
1779
|
}
|
|
1764
1780
|
try {
|
|
1765
1781
|
const net = await import("net");
|
|
1766
|
-
return await new Promise((
|
|
1782
|
+
return await new Promise((resolve2) => {
|
|
1767
1783
|
const smtpPort = parseInt(process.env.SMTP_PORT || "25", 10);
|
|
1768
1784
|
const smtpHost = process.env.SMTP_HOST || "localhost";
|
|
1769
1785
|
const socket = net.createConnection({ host: smtpHost, port: smtpPort, timeout: 3e3 }, () => {
|
|
1770
1786
|
socket.destroy();
|
|
1771
|
-
|
|
1787
|
+
resolve2(true);
|
|
1772
1788
|
});
|
|
1773
1789
|
socket.on("error", () => {
|
|
1774
1790
|
socket.destroy();
|
|
1775
|
-
|
|
1791
|
+
resolve2(false);
|
|
1776
1792
|
});
|
|
1777
1793
|
socket.on("timeout", () => {
|
|
1778
1794
|
socket.destroy();
|
|
1779
|
-
|
|
1795
|
+
resolve2(false);
|
|
1780
1796
|
});
|
|
1781
1797
|
});
|
|
1782
1798
|
} catch {
|
|
@@ -1848,8 +1864,8 @@ var StalwartAdmin = class {
|
|
|
1848
1864
|
}
|
|
1849
1865
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1850
1866
|
const { homedir: homedir11 } = await import("os");
|
|
1851
|
-
const { join:
|
|
1852
|
-
const configPath =
|
|
1867
|
+
const { join: join13 } = await import("path");
|
|
1868
|
+
const configPath = join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1853
1869
|
try {
|
|
1854
1870
|
let config = readFileSync7(configPath, "utf-8");
|
|
1855
1871
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
@@ -1863,14 +1879,14 @@ var StalwartAdmin = class {
|
|
|
1863
1879
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1864
1880
|
get configPath() {
|
|
1865
1881
|
const { homedir: homedir11 } = require("os");
|
|
1866
|
-
const { join:
|
|
1867
|
-
return
|
|
1882
|
+
const { join: join13 } = require("path");
|
|
1883
|
+
return join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1868
1884
|
}
|
|
1869
1885
|
/** Path to host-side DKIM key directory */
|
|
1870
1886
|
get dkimDir() {
|
|
1871
1887
|
const { homedir: homedir11 } = require("os");
|
|
1872
|
-
const { join:
|
|
1873
|
-
return
|
|
1888
|
+
const { join: join13 } = require("path");
|
|
1889
|
+
return join13(homedir11(), ".agenticmail");
|
|
1874
1890
|
}
|
|
1875
1891
|
/**
|
|
1876
1892
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1973,9 +1989,9 @@ var StalwartAdmin = class {
|
|
|
1973
1989
|
async configureOutboundRelay(config) {
|
|
1974
1990
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1975
1991
|
const { homedir: homedir11 } = await import("os");
|
|
1976
|
-
const { join:
|
|
1992
|
+
const { join: join13 } = await import("path");
|
|
1977
1993
|
const routeName = config.routeName ?? "gmail";
|
|
1978
|
-
const tomlPath =
|
|
1994
|
+
const tomlPath = join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1979
1995
|
let toml = readFileSync7(tomlPath, "utf-8");
|
|
1980
1996
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1981
1997
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
@@ -3083,7 +3099,8 @@ var OUTBOUND_TEXT_RULES = [
|
|
|
3083
3099
|
var HIGH_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".env", ".credentials", ".keystore", ".jks", ".p8"]);
|
|
3084
3100
|
var MEDIUM_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".db", ".sqlite", ".sqlite3", ".sql", ".csv", ".tsv", ".json", ".yml", ".yaml", ".conf", ".config", ".ini"]);
|
|
3085
3101
|
function stripHtmlTags(html) {
|
|
3086
|
-
|
|
3102
|
+
const bounded = html.length > 1048576 ? html.slice(0, 1048576) : html;
|
|
3103
|
+
return bounded.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/�?39;/g, "'").replace(/ /g, " ").replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)));
|
|
3087
3104
|
}
|
|
3088
3105
|
var TEXT_SCANNABLE_TYPES = /* @__PURE__ */ new Set([
|
|
3089
3106
|
"text/plain",
|
|
@@ -4616,7 +4633,7 @@ var CloudflareClient = class {
|
|
|
4616
4633
|
// --- Workers methods ---
|
|
4617
4634
|
/** Deploy an Email Worker script (ES module format) */
|
|
4618
4635
|
async deployEmailWorker(scriptName, scriptContent, envVars = {}) {
|
|
4619
|
-
const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/scripts/${scriptName}`;
|
|
4636
|
+
const url = `${CF_API_BASE}/accounts/${encodeURIComponent(this.accountId)}/workers/scripts/${encodeURIComponent(scriptName)}`;
|
|
4620
4637
|
const bindings = Object.entries(envVars).map(([name, text2]) => ({
|
|
4621
4638
|
type: "plain_text",
|
|
4622
4639
|
name,
|
|
@@ -5034,7 +5051,7 @@ var TunnelManager = class {
|
|
|
5034
5051
|
detached: false,
|
|
5035
5052
|
env: { ...process.env, TUNNEL_TOKEN: tunnelToken }
|
|
5036
5053
|
});
|
|
5037
|
-
await new Promise((
|
|
5054
|
+
await new Promise((resolve2, reject) => {
|
|
5038
5055
|
let resolved = false;
|
|
5039
5056
|
const timeout = setTimeout(() => {
|
|
5040
5057
|
if (!resolved) {
|
|
@@ -5048,7 +5065,7 @@ var TunnelManager = class {
|
|
|
5048
5065
|
resolved = true;
|
|
5049
5066
|
clearTimeout(timeout);
|
|
5050
5067
|
this.running = true;
|
|
5051
|
-
|
|
5068
|
+
resolve2();
|
|
5052
5069
|
}
|
|
5053
5070
|
};
|
|
5054
5071
|
this.process.stderr?.on("data", onData);
|
|
@@ -5087,8 +5104,8 @@ var TunnelManager = class {
|
|
|
5087
5104
|
this.process = null;
|
|
5088
5105
|
p.kill("SIGTERM");
|
|
5089
5106
|
await Promise.race([
|
|
5090
|
-
new Promise((
|
|
5091
|
-
new Promise((
|
|
5107
|
+
new Promise((resolve2) => p.on("exit", () => resolve2())),
|
|
5108
|
+
new Promise((resolve2) => setTimeout(resolve2, 5e3))
|
|
5092
5109
|
]);
|
|
5093
5110
|
this.running = false;
|
|
5094
5111
|
}
|
|
@@ -5144,7 +5161,10 @@ function parseGoogleVoiceSms(emailBody, emailFrom) {
|
|
|
5144
5161
|
if (!emailBody || typeof emailBody !== "string") return null;
|
|
5145
5162
|
if (!emailFrom || typeof emailFrom !== "string") return null;
|
|
5146
5163
|
const fromLower = emailFrom.toLowerCase();
|
|
5147
|
-
const
|
|
5164
|
+
const atIdx = fromLower.lastIndexOf("@");
|
|
5165
|
+
const domain = atIdx >= 0 ? fromLower.slice(atIdx + 1).replace(/[>"'\s].*$/, "") : "";
|
|
5166
|
+
const isGoogleDomain = domain === "google.com" || domain.endsWith(".google.com");
|
|
5167
|
+
const isGoogleVoice = isGoogleDomain && (fromLower.startsWith("voice-noreply@") || domain === "txt.voice.google.com" || domain === "voice.google.com" || domain.endsWith(".voice.google.com") || fromLower.includes("voice"));
|
|
5148
5168
|
if (!isGoogleVoice) return null;
|
|
5149
5169
|
let text = emailBody.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<\/div>/gi, "\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").trim();
|
|
5150
5170
|
let from = "";
|
|
@@ -6175,6 +6195,15 @@ var GatewayManager = class {
|
|
|
6175
6195
|
bcc: mail.bcc ? Array.isArray(mail.bcc) ? mail.bcc.join(", ") : mail.bcc : void 0,
|
|
6176
6196
|
subject: mail.subject,
|
|
6177
6197
|
text: mail.text || void 0,
|
|
6198
|
+
// The `html` field is the literal HTML body of the outbound
|
|
6199
|
+
// mail — by design it is whatever the sender chose to compose.
|
|
6200
|
+
// CodeQL `js/xss` flags this because the value flows from user
|
|
6201
|
+
// input, but nodemailer is the SMTP serializer, not an HTML
|
|
6202
|
+
// renderer; XSS would only occur if the recipient's MUA
|
|
6203
|
+
// executed the body, which is outside our trust boundary.
|
|
6204
|
+
// The outbound-guard (packages/core/src/mail/outbound-guard.ts)
|
|
6205
|
+
// already scores HTML bodies for suspicious patterns at the
|
|
6206
|
+
// pre-send step. lgtm[js/xss]
|
|
6178
6207
|
html: mail.html || void 0,
|
|
6179
6208
|
replyTo: mail.replyTo || from,
|
|
6180
6209
|
inReplyTo: mail.inReplyTo || void 0,
|
|
@@ -6536,11 +6565,11 @@ var RelayBridge = class {
|
|
|
6536
6565
|
this.options = options;
|
|
6537
6566
|
}
|
|
6538
6567
|
async start() {
|
|
6539
|
-
return new Promise((
|
|
6568
|
+
return new Promise((resolve2, reject) => {
|
|
6540
6569
|
this.server = (0, import_node_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
6541
6570
|
this.server.listen(this.options.port, "127.0.0.1", () => {
|
|
6542
6571
|
console.log(`[RelayBridge] Listening on 127.0.0.1:${this.options.port}`);
|
|
6543
|
-
|
|
6572
|
+
resolve2();
|
|
6544
6573
|
});
|
|
6545
6574
|
this.server.on("error", reject);
|
|
6546
6575
|
});
|
|
@@ -6742,16 +6771,161 @@ try {
|
|
|
6742
6771
|
} catch {
|
|
6743
6772
|
}
|
|
6744
6773
|
|
|
6774
|
+
// src/util/safe-path.ts
|
|
6775
|
+
var import_node_path5 = require("path");
|
|
6776
|
+
var PathTraversalError = class extends Error {
|
|
6777
|
+
constructor(baseDir, parts) {
|
|
6778
|
+
super(
|
|
6779
|
+
`path traversal attempt: ${JSON.stringify(parts)} resolves outside ${baseDir}`
|
|
6780
|
+
);
|
|
6781
|
+
this.baseDir = baseDir;
|
|
6782
|
+
this.parts = parts;
|
|
6783
|
+
this.name = "PathTraversalError";
|
|
6784
|
+
}
|
|
6785
|
+
};
|
|
6786
|
+
function safeJoin(baseDir, ...partsAndOpts) {
|
|
6787
|
+
let opts = {};
|
|
6788
|
+
const parts = [];
|
|
6789
|
+
for (const p of partsAndOpts) {
|
|
6790
|
+
if (typeof p === "string") {
|
|
6791
|
+
parts.push(p);
|
|
6792
|
+
} else if (p && typeof p === "object") {
|
|
6793
|
+
opts = p;
|
|
6794
|
+
}
|
|
6795
|
+
}
|
|
6796
|
+
if (!opts.allowAbsolute) {
|
|
6797
|
+
for (const part of parts) {
|
|
6798
|
+
if ((0, import_node_path5.isAbsolute)(part)) {
|
|
6799
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
const resolvedBase = (0, import_node_path5.resolve)(baseDir);
|
|
6804
|
+
const resolved = (0, import_node_path5.resolve)(resolvedBase, ...parts);
|
|
6805
|
+
if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + import_node_path5.sep)) {
|
|
6806
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6807
|
+
}
|
|
6808
|
+
return resolved;
|
|
6809
|
+
}
|
|
6810
|
+
function tryJoin(baseDir, ...parts) {
|
|
6811
|
+
try {
|
|
6812
|
+
return safeJoin(baseDir, ...parts);
|
|
6813
|
+
} catch (err) {
|
|
6814
|
+
if (err instanceof PathTraversalError) return null;
|
|
6815
|
+
throw err;
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
function assertWithinBase(baseDir, candidate) {
|
|
6819
|
+
const resolvedBase = (0, import_node_path5.resolve)(baseDir);
|
|
6820
|
+
const resolvedCandidate = (0, import_node_path5.resolve)(candidate);
|
|
6821
|
+
if (resolvedCandidate !== resolvedBase && !resolvedCandidate.startsWith(resolvedBase + import_node_path5.sep)) {
|
|
6822
|
+
throw new PathTraversalError(baseDir, [candidate]);
|
|
6823
|
+
}
|
|
6824
|
+
return resolvedCandidate;
|
|
6825
|
+
}
|
|
6826
|
+
|
|
6827
|
+
// src/util/redact.ts
|
|
6828
|
+
var REDACTED = "***";
|
|
6829
|
+
function redactSecret(value) {
|
|
6830
|
+
if (typeof value !== "string") return REDACTED;
|
|
6831
|
+
if (value.length === 0) return REDACTED;
|
|
6832
|
+
for (const prefix of ["mk_", "ak_", "sk-", "pk_", "tok_"]) {
|
|
6833
|
+
if (value.startsWith(prefix)) return `${prefix}${REDACTED}`;
|
|
6834
|
+
}
|
|
6835
|
+
return REDACTED;
|
|
6836
|
+
}
|
|
6837
|
+
var SENSITIVE_KEY_PATTERNS = [
|
|
6838
|
+
/key$/i,
|
|
6839
|
+
/secret/i,
|
|
6840
|
+
/password/i,
|
|
6841
|
+
/passwd/i,
|
|
6842
|
+
/token/i,
|
|
6843
|
+
/authorization/i,
|
|
6844
|
+
/bearer/i,
|
|
6845
|
+
/\bapikey\b/i,
|
|
6846
|
+
/\bapi_key\b/i,
|
|
6847
|
+
/\bmasterkey\b/i,
|
|
6848
|
+
/\bmaster_key\b/i
|
|
6849
|
+
];
|
|
6850
|
+
function looksSensitive(key) {
|
|
6851
|
+
return SENSITIVE_KEY_PATTERNS.some((p) => p.test(key));
|
|
6852
|
+
}
|
|
6853
|
+
function redactObject(input, _depth = 0) {
|
|
6854
|
+
if (_depth > 12) return input;
|
|
6855
|
+
if (input === null || input === void 0) return input;
|
|
6856
|
+
if (typeof input !== "object") return input;
|
|
6857
|
+
if (Array.isArray(input)) {
|
|
6858
|
+
return input.map((v) => redactObject(v, _depth + 1));
|
|
6859
|
+
}
|
|
6860
|
+
const proto = Object.getPrototypeOf(input);
|
|
6861
|
+
if (proto !== Object.prototype && proto !== null) return input;
|
|
6862
|
+
const out = {};
|
|
6863
|
+
for (const [k, v] of Object.entries(input)) {
|
|
6864
|
+
if (looksSensitive(k)) {
|
|
6865
|
+
out[k] = typeof v === "string" ? redactSecret(v) : REDACTED;
|
|
6866
|
+
} else {
|
|
6867
|
+
out[k] = redactObject(v, _depth + 1);
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
return out;
|
|
6871
|
+
}
|
|
6872
|
+
|
|
6873
|
+
// src/util/safe-url.ts
|
|
6874
|
+
var UnsafeApiUrlError = class extends Error {
|
|
6875
|
+
constructor(raw, reason) {
|
|
6876
|
+
super(`unsafe AgenticMail API URL ${JSON.stringify(raw)}: ${reason}`);
|
|
6877
|
+
this.raw = raw;
|
|
6878
|
+
this.reason = reason;
|
|
6879
|
+
this.name = "UnsafeApiUrlError";
|
|
6880
|
+
}
|
|
6881
|
+
};
|
|
6882
|
+
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
6883
|
+
"169.254.169.254",
|
|
6884
|
+
// AWS / Azure / GCP IPv4 metadata
|
|
6885
|
+
"fd00:ec2::254",
|
|
6886
|
+
// AWS IPv6 metadata
|
|
6887
|
+
"metadata.google.internal",
|
|
6888
|
+
// GCP DNS
|
|
6889
|
+
"metadata.azure.internal"
|
|
6890
|
+
// Azure DNS
|
|
6891
|
+
]);
|
|
6892
|
+
function validateApiUrl(raw) {
|
|
6893
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
6894
|
+
throw new UnsafeApiUrlError(String(raw), "must be a non-empty string");
|
|
6895
|
+
}
|
|
6896
|
+
let parsed;
|
|
6897
|
+
try {
|
|
6898
|
+
parsed = new URL(raw);
|
|
6899
|
+
} catch (err) {
|
|
6900
|
+
throw new UnsafeApiUrlError(raw, `unparseable: ${err.message}`);
|
|
6901
|
+
}
|
|
6902
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
6903
|
+
throw new UnsafeApiUrlError(raw, `unsupported protocol: ${parsed.protocol}`);
|
|
6904
|
+
}
|
|
6905
|
+
const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
6906
|
+
if (BLOCKED_HOSTS.has(host)) {
|
|
6907
|
+
throw new UnsafeApiUrlError(raw, `blocked metadata host: ${host}`);
|
|
6908
|
+
}
|
|
6909
|
+
if (parsed.username || parsed.password) {
|
|
6910
|
+
throw new UnsafeApiUrlError(raw, "embedded credentials are not supported");
|
|
6911
|
+
}
|
|
6912
|
+
return parsed.origin;
|
|
6913
|
+
}
|
|
6914
|
+
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6915
|
+
const path = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6916
|
+
return new URL(path, baseOrigin + "/").toString();
|
|
6917
|
+
}
|
|
6918
|
+
|
|
6745
6919
|
// src/setup/index.ts
|
|
6746
6920
|
var import_node_crypto3 = require("crypto");
|
|
6747
6921
|
var import_node_fs7 = require("fs");
|
|
6748
|
-
var
|
|
6922
|
+
var import_node_path9 = require("path");
|
|
6749
6923
|
var import_node_os7 = require("os");
|
|
6750
6924
|
|
|
6751
6925
|
// src/setup/deps.ts
|
|
6752
6926
|
var import_node_child_process2 = require("child_process");
|
|
6753
6927
|
var import_node_fs4 = require("fs");
|
|
6754
|
-
var
|
|
6928
|
+
var import_node_path6 = require("path");
|
|
6755
6929
|
var import_node_os4 = require("os");
|
|
6756
6930
|
var DependencyChecker = class {
|
|
6757
6931
|
async checkAll() {
|
|
@@ -6802,7 +6976,7 @@ var DependencyChecker = class {
|
|
|
6802
6976
|
}
|
|
6803
6977
|
}
|
|
6804
6978
|
async checkCloudflared() {
|
|
6805
|
-
const binPath = (0,
|
|
6979
|
+
const binPath = (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".agenticmail", "bin", "cloudflared");
|
|
6806
6980
|
if ((0, import_node_fs4.existsSync)(binPath)) {
|
|
6807
6981
|
let version;
|
|
6808
6982
|
try {
|
|
@@ -6827,12 +7001,12 @@ var DependencyChecker = class {
|
|
|
6827
7001
|
var import_node_child_process3 = require("child_process");
|
|
6828
7002
|
var import_node_fs5 = require("fs");
|
|
6829
7003
|
var import_promises2 = require("fs/promises");
|
|
6830
|
-
var
|
|
7004
|
+
var import_node_path7 = require("path");
|
|
6831
7005
|
var import_node_os5 = require("os");
|
|
6832
7006
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6833
7007
|
const maxLines = opts.maxLines ?? 20;
|
|
6834
7008
|
const timeout = opts.timeout ?? 3e5;
|
|
6835
|
-
return new Promise((
|
|
7009
|
+
return new Promise((resolve2, reject) => {
|
|
6836
7010
|
const child = (0, import_node_child_process3.spawn)("sh", ["-c", cmd], {
|
|
6837
7011
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6838
7012
|
timeout
|
|
@@ -6874,7 +7048,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6874
7048
|
}
|
|
6875
7049
|
process.stdout.write(`\x1B[${displayedCount}A`);
|
|
6876
7050
|
}
|
|
6877
|
-
|
|
7051
|
+
resolve2({ exitCode: code ?? 1, fullOutput });
|
|
6878
7052
|
});
|
|
6879
7053
|
child.on("error", (err) => {
|
|
6880
7054
|
if (displayedCount > 0) {
|
|
@@ -6890,7 +7064,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6890
7064
|
}
|
|
6891
7065
|
function runSilent(command, args, opts = {}) {
|
|
6892
7066
|
const timeout = opts.timeout ?? 3e5;
|
|
6893
|
-
return new Promise((
|
|
7067
|
+
return new Promise((resolve2, reject) => {
|
|
6894
7068
|
const child = (0, import_node_child_process3.spawn)(command, args, {
|
|
6895
7069
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6896
7070
|
timeout
|
|
@@ -6902,7 +7076,7 @@ function runSilent(command, args, opts = {}) {
|
|
|
6902
7076
|
child.stderr?.on("data", (d) => {
|
|
6903
7077
|
fullOutput += d.toString();
|
|
6904
7078
|
});
|
|
6905
|
-
child.on("close", (code) =>
|
|
7079
|
+
child.on("close", (code) => resolve2({ exitCode: code ?? 1, fullOutput }));
|
|
6906
7080
|
child.on("error", reject);
|
|
6907
7081
|
});
|
|
6908
7082
|
}
|
|
@@ -7004,8 +7178,8 @@ var DependencyInstaller = class {
|
|
|
7004
7178
|
try {
|
|
7005
7179
|
const composeBin = (0, import_node_child_process3.execFileSync)("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7006
7180
|
if (!composeBin) return;
|
|
7007
|
-
const pluginDir = (0,
|
|
7008
|
-
const pluginPath = (0,
|
|
7181
|
+
const pluginDir = (0, import_node_path7.join)((0, import_node_os5.homedir)(), ".docker", "cli-plugins");
|
|
7182
|
+
const pluginPath = (0, import_node_path7.join)(pluginDir, "docker-compose");
|
|
7009
7183
|
if ((0, import_node_fs5.existsSync)(pluginPath)) return;
|
|
7010
7184
|
try {
|
|
7011
7185
|
(0, import_node_fs5.mkdirSync)(pluginDir, { recursive: true });
|
|
@@ -7071,9 +7245,9 @@ var DependencyInstaller = class {
|
|
|
7071
7245
|
return;
|
|
7072
7246
|
}
|
|
7073
7247
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
7074
|
-
const tmpDir = (0,
|
|
7248
|
+
const tmpDir = (0, import_node_path7.join)((0, import_node_os5.homedir)(), ".agenticmail", "tmp");
|
|
7075
7249
|
await (0, import_promises2.mkdir)(tmpDir, { recursive: true });
|
|
7076
|
-
const scriptPath = (0,
|
|
7250
|
+
const scriptPath = (0, import_node_path7.join)(tmpDir, "install-docker.sh");
|
|
7077
7251
|
const dlResult = await runShellWithRollingOutput(
|
|
7078
7252
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
7079
7253
|
{ timeout: 3e5 }
|
|
@@ -7141,7 +7315,7 @@ var DependencyInstaller = class {
|
|
|
7141
7315
|
}
|
|
7142
7316
|
try {
|
|
7143
7317
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
7144
|
-
const dockerExe = (0,
|
|
7318
|
+
const dockerExe = (0, import_node_path7.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
7145
7319
|
if ((0, import_node_fs5.existsSync)(dockerExe)) {
|
|
7146
7320
|
(0, import_node_child_process3.execSync)(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
7147
7321
|
}
|
|
@@ -7192,7 +7366,7 @@ var DependencyInstaller = class {
|
|
|
7192
7366
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
7193
7367
|
try {
|
|
7194
7368
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
7195
|
-
const dockerExe = (0,
|
|
7369
|
+
const dockerExe = (0, import_node_path7.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
7196
7370
|
if ((0, import_node_fs5.existsSync)(dockerExe)) {
|
|
7197
7371
|
(0, import_node_child_process3.execSync)(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
7198
7372
|
}
|
|
@@ -7289,9 +7463,9 @@ var DependencyInstaller = class {
|
|
|
7289
7463
|
*/
|
|
7290
7464
|
async installCloudflared() {
|
|
7291
7465
|
const os = (0, import_node_os5.platform)();
|
|
7292
|
-
const binDir = (0,
|
|
7466
|
+
const binDir = (0, import_node_path7.join)((0, import_node_os5.homedir)(), ".agenticmail", "bin");
|
|
7293
7467
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
7294
|
-
const binPath = (0,
|
|
7468
|
+
const binPath = (0, import_node_path7.join)(binDir, binName);
|
|
7295
7469
|
if ((0, import_node_fs5.existsSync)(binPath)) {
|
|
7296
7470
|
return binPath;
|
|
7297
7471
|
}
|
|
@@ -7321,7 +7495,7 @@ var DependencyInstaller = class {
|
|
|
7321
7495
|
}
|
|
7322
7496
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
7323
7497
|
if (os === "darwin") {
|
|
7324
|
-
const tgzPath = (0,
|
|
7498
|
+
const tgzPath = (0, import_node_path7.join)(binDir, "cloudflared.tgz");
|
|
7325
7499
|
await (0, import_promises2.writeFile)(tgzPath, buffer);
|
|
7326
7500
|
try {
|
|
7327
7501
|
(0, import_node_child_process3.execFileSync)("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
@@ -7357,7 +7531,7 @@ var DependencyInstaller = class {
|
|
|
7357
7531
|
// src/setup/service.ts
|
|
7358
7532
|
var import_node_child_process4 = require("child_process");
|
|
7359
7533
|
var import_node_fs6 = require("fs");
|
|
7360
|
-
var
|
|
7534
|
+
var import_node_path8 = require("path");
|
|
7361
7535
|
var import_node_os6 = require("os");
|
|
7362
7536
|
var import_node_module2 = require("module");
|
|
7363
7537
|
var import_meta2 = {};
|
|
@@ -7370,9 +7544,9 @@ var ServiceManager = class {
|
|
|
7370
7544
|
*/
|
|
7371
7545
|
getServicePath() {
|
|
7372
7546
|
if (this.os === "darwin") {
|
|
7373
|
-
return (0,
|
|
7547
|
+
return (0, import_node_path8.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
7374
7548
|
} else {
|
|
7375
|
-
return (0,
|
|
7549
|
+
return (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
7376
7550
|
}
|
|
7377
7551
|
}
|
|
7378
7552
|
/**
|
|
@@ -7410,33 +7584,33 @@ var ServiceManager = class {
|
|
|
7410
7584
|
} catch {
|
|
7411
7585
|
}
|
|
7412
7586
|
const parentPackages = [
|
|
7413
|
-
(0,
|
|
7587
|
+
(0, import_node_path8.join)("@agenticmail", "cli"),
|
|
7414
7588
|
// current scoped package
|
|
7415
7589
|
"agenticmail"
|
|
7416
7590
|
// legacy unscoped package
|
|
7417
7591
|
];
|
|
7418
7592
|
const baseDirs = [
|
|
7419
7593
|
// user-local install
|
|
7420
|
-
(0,
|
|
7594
|
+
(0, import_node_path8.join)((0, import_node_os6.homedir)(), "node_modules")
|
|
7421
7595
|
];
|
|
7422
7596
|
try {
|
|
7423
7597
|
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7424
|
-
baseDirs.push((0,
|
|
7425
|
-
baseDirs.push((0,
|
|
7598
|
+
baseDirs.push((0, import_node_path8.join)(prefix, "lib", "node_modules"));
|
|
7599
|
+
baseDirs.push((0, import_node_path8.join)(prefix, "node_modules"));
|
|
7426
7600
|
} catch {
|
|
7427
7601
|
}
|
|
7428
7602
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
7429
7603
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
7430
7604
|
for (const base of baseDirs) {
|
|
7431
|
-
const sibling = (0,
|
|
7605
|
+
const sibling = (0, import_node_path8.join)(base, "@agenticmail", "api", "dist", "index.js");
|
|
7432
7606
|
if ((0, import_node_fs6.existsSync)(sibling)) return sibling;
|
|
7433
7607
|
for (const parent of parentPackages) {
|
|
7434
|
-
const nested = (0,
|
|
7608
|
+
const nested = (0, import_node_path8.join)(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
7435
7609
|
if ((0, import_node_fs6.existsSync)(nested)) return nested;
|
|
7436
7610
|
}
|
|
7437
7611
|
}
|
|
7438
|
-
const dataDir = (0,
|
|
7439
|
-
const entryCache = (0,
|
|
7612
|
+
const dataDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail");
|
|
7613
|
+
const entryCache = (0, import_node_path8.join)(dataDir, "api-entry.path");
|
|
7440
7614
|
if ((0, import_node_fs6.existsSync)(entryCache)) {
|
|
7441
7615
|
const cached = (0, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
|
|
7442
7616
|
if (cached && (0, import_node_fs6.existsSync)(cached)) return cached;
|
|
@@ -7447,9 +7621,9 @@ var ServiceManager = class {
|
|
|
7447
7621
|
* Cache the API entry path so the service can find it later.
|
|
7448
7622
|
*/
|
|
7449
7623
|
cacheApiEntryPath(entryPath) {
|
|
7450
|
-
const dataDir = (0,
|
|
7624
|
+
const dataDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail");
|
|
7451
7625
|
if (!(0, import_node_fs6.existsSync)(dataDir)) (0, import_node_fs6.mkdirSync)(dataDir, { recursive: true });
|
|
7452
|
-
(0, import_node_fs6.writeFileSync)((0,
|
|
7626
|
+
(0, import_node_fs6.writeFileSync)((0, import_node_path8.join)(dataDir, "api-entry.path"), entryPath);
|
|
7453
7627
|
}
|
|
7454
7628
|
/**
|
|
7455
7629
|
* Get the current package version.
|
|
@@ -7470,7 +7644,7 @@ var ServiceManager = class {
|
|
|
7470
7644
|
}
|
|
7471
7645
|
try {
|
|
7472
7646
|
const apiEntry = this.getApiEntryPath();
|
|
7473
|
-
const apiPkg = (0,
|
|
7647
|
+
const apiPkg = (0, import_node_path8.join)(apiEntry, "..", "..", "package.json");
|
|
7474
7648
|
if ((0, import_node_fs6.existsSync)(apiPkg)) {
|
|
7475
7649
|
const pkg = JSON.parse((0, import_node_fs6.readFileSync)(apiPkg, "utf-8"));
|
|
7476
7650
|
if (pkg.version) return pkg.version;
|
|
@@ -7478,14 +7652,14 @@ var ServiceManager = class {
|
|
|
7478
7652
|
} catch {
|
|
7479
7653
|
}
|
|
7480
7654
|
const candidates = [
|
|
7481
|
-
(0,
|
|
7482
|
-
(0,
|
|
7483
|
-
(0,
|
|
7655
|
+
(0, import_node_path8.join)((0, import_node_os6.homedir)(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7656
|
+
(0, import_node_path8.join)((0, import_node_os6.homedir)(), "node_modules", "agenticmail", "package.json"),
|
|
7657
|
+
(0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "package-version.json")
|
|
7484
7658
|
];
|
|
7485
7659
|
try {
|
|
7486
7660
|
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7487
|
-
candidates.push((0,
|
|
7488
|
-
candidates.push((0,
|
|
7661
|
+
candidates.push((0, import_node_path8.join)(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
7662
|
+
candidates.push((0, import_node_path8.join)(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
7489
7663
|
} catch {
|
|
7490
7664
|
}
|
|
7491
7665
|
for (const p of candidates) {
|
|
@@ -7504,8 +7678,8 @@ var ServiceManager = class {
|
|
|
7504
7678
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
7505
7679
|
*/
|
|
7506
7680
|
generateStartScript(nodePath, apiEntry) {
|
|
7507
|
-
const scriptPath = (0,
|
|
7508
|
-
const scriptDir = (0,
|
|
7681
|
+
const scriptPath = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin", "start-server.sh");
|
|
7682
|
+
const scriptDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin");
|
|
7509
7683
|
if (!(0, import_node_fs6.existsSync)(scriptDir)) (0, import_node_fs6.mkdirSync)(scriptDir, { recursive: true });
|
|
7510
7684
|
const script = [
|
|
7511
7685
|
"#!/bin/bash",
|
|
@@ -7571,7 +7745,7 @@ var ServiceManager = class {
|
|
|
7571
7745
|
*/
|
|
7572
7746
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
7573
7747
|
const config = JSON.parse((0, import_node_fs6.readFileSync)(configPath, "utf-8"));
|
|
7574
|
-
const logDir = (0,
|
|
7748
|
+
const logDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "logs");
|
|
7575
7749
|
if (!(0, import_node_fs6.existsSync)(logDir)) (0, import_node_fs6.mkdirSync)(logDir, { recursive: true });
|
|
7576
7750
|
const version = this.getVersion();
|
|
7577
7751
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
@@ -7595,7 +7769,7 @@ var ServiceManager = class {
|
|
|
7595
7769
|
<key>HOME</key>
|
|
7596
7770
|
<string>${(0, import_node_os6.homedir)()}</string>
|
|
7597
7771
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
7598
|
-
<string>${config.dataDir || (0,
|
|
7772
|
+
<string>${config.dataDir || (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail")}</string>
|
|
7599
7773
|
<key>PATH</key>
|
|
7600
7774
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
7601
7775
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -7649,7 +7823,7 @@ var ServiceManager = class {
|
|
|
7649
7823
|
*/
|
|
7650
7824
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
7651
7825
|
const config = JSON.parse((0, import_node_fs6.readFileSync)(configPath, "utf-8"));
|
|
7652
|
-
const dataDir = config.dataDir || (0,
|
|
7826
|
+
const dataDir = config.dataDir || (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail");
|
|
7653
7827
|
const version = this.getVersion();
|
|
7654
7828
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7655
7829
|
return `[Unit]
|
|
@@ -7679,7 +7853,7 @@ WantedBy=default.target
|
|
|
7679
7853
|
* Install the auto-start service.
|
|
7680
7854
|
*/
|
|
7681
7855
|
install() {
|
|
7682
|
-
const configPath = (0,
|
|
7856
|
+
const configPath = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "config.json");
|
|
7683
7857
|
if (!(0, import_node_fs6.existsSync)(configPath)) {
|
|
7684
7858
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
7685
7859
|
}
|
|
@@ -7692,7 +7866,7 @@ WantedBy=default.target
|
|
|
7692
7866
|
}
|
|
7693
7867
|
const servicePath = this.getServicePath();
|
|
7694
7868
|
if (this.os === "darwin") {
|
|
7695
|
-
const dir = (0,
|
|
7869
|
+
const dir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents");
|
|
7696
7870
|
if (!(0, import_node_fs6.existsSync)(dir)) (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
|
|
7697
7871
|
if ((0, import_node_fs6.existsSync)(servicePath)) {
|
|
7698
7872
|
try {
|
|
@@ -7710,7 +7884,7 @@ WantedBy=default.target
|
|
|
7710
7884
|
}
|
|
7711
7885
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
7712
7886
|
} else if (this.os === "linux") {
|
|
7713
|
-
const dir = (0,
|
|
7887
|
+
const dir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user");
|
|
7714
7888
|
if (!(0, import_node_fs6.existsSync)(dir)) (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
|
|
7715
7889
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
7716
7890
|
(0, import_node_fs6.writeFileSync)(servicePath, unit);
|
|
@@ -7836,7 +8010,7 @@ WantedBy=default.target
|
|
|
7836
8010
|
} catch {
|
|
7837
8011
|
return { reason: "Service file unreadable" };
|
|
7838
8012
|
}
|
|
7839
|
-
const startScript = (0,
|
|
8013
|
+
const startScript = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin", "start-server.sh");
|
|
7840
8014
|
if (serviceContent.includes(startScript)) {
|
|
7841
8015
|
if (!(0, import_node_fs6.existsSync)(startScript)) {
|
|
7842
8016
|
return { reason: "start-server.sh is missing" };
|
|
@@ -7869,7 +8043,7 @@ WantedBy=default.target
|
|
|
7869
8043
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7870
8044
|
}
|
|
7871
8045
|
}
|
|
7872
|
-
const entryCache = (0,
|
|
8046
|
+
const entryCache = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "api-entry.path");
|
|
7873
8047
|
if ((0, import_node_fs6.existsSync)(entryCache)) {
|
|
7874
8048
|
try {
|
|
7875
8049
|
const cached = (0, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
|
|
@@ -7924,12 +8098,12 @@ var SetupManager = class {
|
|
|
7924
8098
|
* falls back to monorepo location.
|
|
7925
8099
|
*/
|
|
7926
8100
|
getComposePath() {
|
|
7927
|
-
const standalonePath = (0,
|
|
8101
|
+
const standalonePath = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "docker-compose.yml");
|
|
7928
8102
|
if ((0, import_node_fs7.existsSync)(standalonePath)) return standalonePath;
|
|
7929
8103
|
const cwd = process.cwd();
|
|
7930
|
-
const candidates = [cwd, (0,
|
|
8104
|
+
const candidates = [cwd, (0, import_node_path9.join)(cwd, "..")];
|
|
7931
8105
|
for (const dir of candidates) {
|
|
7932
|
-
const p = (0,
|
|
8106
|
+
const p = (0, import_node_path9.join)(dir, "docker-compose.yml");
|
|
7933
8107
|
if ((0, import_node_fs7.existsSync)(p)) return p;
|
|
7934
8108
|
}
|
|
7935
8109
|
return standalonePath;
|
|
@@ -7940,9 +8114,9 @@ var SetupManager = class {
|
|
|
7940
8114
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7941
8115
|
*/
|
|
7942
8116
|
initConfig() {
|
|
7943
|
-
const dataDir = (0,
|
|
7944
|
-
const configPath = (0,
|
|
7945
|
-
const envPath = (0,
|
|
8117
|
+
const dataDir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail");
|
|
8118
|
+
const configPath = (0, import_node_path9.join)(dataDir, "config.json");
|
|
8119
|
+
const envPath = (0, import_node_path9.join)(dataDir, ".env");
|
|
7946
8120
|
if ((0, import_node_fs7.existsSync)(configPath)) {
|
|
7947
8121
|
try {
|
|
7948
8122
|
const existing = JSON.parse((0, import_node_fs7.readFileSync)(configPath, "utf-8"));
|
|
@@ -7994,12 +8168,12 @@ IMAP_PORT=143
|
|
|
7994
8168
|
* with the correct admin password from config.
|
|
7995
8169
|
*/
|
|
7996
8170
|
generateDockerFiles(config) {
|
|
7997
|
-
const dataDir = config.dataDir || (0,
|
|
8171
|
+
const dataDir = config.dataDir || (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail");
|
|
7998
8172
|
if (!(0, import_node_fs7.existsSync)(dataDir)) {
|
|
7999
8173
|
(0, import_node_fs7.mkdirSync)(dataDir, { recursive: true });
|
|
8000
8174
|
}
|
|
8001
8175
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
8002
|
-
const composePath = (0,
|
|
8176
|
+
const composePath = (0, import_node_path9.join)(dataDir, "docker-compose.yml");
|
|
8003
8177
|
(0, import_node_fs7.writeFileSync)(composePath, `services:
|
|
8004
8178
|
stalwart:
|
|
8005
8179
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
@@ -8030,7 +8204,7 @@ volumes:
|
|
|
8030
8204
|
stalwart-data:
|
|
8031
8205
|
`);
|
|
8032
8206
|
(0, import_node_fs7.chmodSync)(composePath, 384);
|
|
8033
|
-
const tomlPath = (0,
|
|
8207
|
+
const tomlPath = (0, import_node_path9.join)(dataDir, "stalwart.toml");
|
|
8034
8208
|
(0, import_node_fs7.writeFileSync)(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
8035
8209
|
|
|
8036
8210
|
[server]
|
|
@@ -8087,7 +8261,7 @@ secret = "${password}"
|
|
|
8087
8261
|
* Check if config has already been initialized.
|
|
8088
8262
|
*/
|
|
8089
8263
|
isInitialized() {
|
|
8090
|
-
const configPath = (0,
|
|
8264
|
+
const configPath = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "config.json");
|
|
8091
8265
|
return (0, import_node_fs7.existsSync)(configPath);
|
|
8092
8266
|
}
|
|
8093
8267
|
};
|
|
@@ -8095,7 +8269,7 @@ secret = "${password}"
|
|
|
8095
8269
|
// src/threading/thread-id.ts
|
|
8096
8270
|
var import_node_crypto4 = require("crypto");
|
|
8097
8271
|
function stripReplyPrefixes(subject) {
|
|
8098
|
-
let s = subject;
|
|
8272
|
+
let s = subject.length > 1e3 ? subject.slice(0, 1e3) : subject;
|
|
8099
8273
|
for (; ; ) {
|
|
8100
8274
|
const next = s.replace(/^\s*(?:re|fwd?|fw)\s*(?:\[\d+\])?\s*:\s*/i, "");
|
|
8101
8275
|
if (next === s) break;
|
|
@@ -8115,8 +8289,9 @@ function normalizeSubject(subject) {
|
|
|
8115
8289
|
}
|
|
8116
8290
|
function normalizeAddress(addr) {
|
|
8117
8291
|
if (!addr) return "(unknown)";
|
|
8118
|
-
const
|
|
8119
|
-
const
|
|
8292
|
+
const bounded = addr.length > 500 ? addr.slice(0, 500) : addr;
|
|
8293
|
+
const m = bounded.match(/<([^>]+)>/);
|
|
8294
|
+
const raw = m ? m[1] : bounded;
|
|
8120
8295
|
return raw.trim().toLowerCase();
|
|
8121
8296
|
}
|
|
8122
8297
|
function threadIdFor(input) {
|
|
@@ -8127,8 +8302,8 @@ function threadIdFor(input) {
|
|
|
8127
8302
|
// src/threading/thread-cache.ts
|
|
8128
8303
|
var import_node_fs8 = require("fs");
|
|
8129
8304
|
var import_node_os8 = require("os");
|
|
8130
|
-
var
|
|
8131
|
-
var CACHE_DIR_DEFAULT = (0,
|
|
8305
|
+
var import_node_path10 = require("path");
|
|
8306
|
+
var CACHE_DIR_DEFAULT = (0, import_node_path10.join)((0, import_node_os8.homedir)(), ".agenticmail", "thread-cache");
|
|
8132
8307
|
var DEFAULT_K_MESSAGES = 10;
|
|
8133
8308
|
var DEFAULT_LRU_CAP = 5e3;
|
|
8134
8309
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -8146,7 +8321,7 @@ var ThreadCache = class {
|
|
|
8146
8321
|
}
|
|
8147
8322
|
}
|
|
8148
8323
|
pathFor(threadId) {
|
|
8149
|
-
return (0,
|
|
8324
|
+
return (0, import_node_path10.join)(this.dir, `${threadId}.json`);
|
|
8150
8325
|
}
|
|
8151
8326
|
read(threadId) {
|
|
8152
8327
|
const p = this.pathFor(threadId);
|
|
@@ -8236,7 +8411,7 @@ var ThreadCache = class {
|
|
|
8236
8411
|
}
|
|
8237
8412
|
if (files.length <= this.lruCap) return;
|
|
8238
8413
|
const stats = files.map((f) => {
|
|
8239
|
-
const p = (0,
|
|
8414
|
+
const p = (0, import_node_path10.join)(this.dir, f);
|
|
8240
8415
|
try {
|
|
8241
8416
|
return { p, mtime: (0, import_node_fs8.statSync)(p).mtimeMs };
|
|
8242
8417
|
} catch {
|
|
@@ -8268,8 +8443,8 @@ function dedupAndCap(messages, k) {
|
|
|
8268
8443
|
// src/threading/agent-memory.ts
|
|
8269
8444
|
var import_node_fs9 = require("fs");
|
|
8270
8445
|
var import_node_os9 = require("os");
|
|
8271
|
-
var
|
|
8272
|
-
var MEMORY_DIR_DEFAULT = (0,
|
|
8446
|
+
var import_node_path11 = require("path");
|
|
8447
|
+
var MEMORY_DIR_DEFAULT = (0, import_node_path11.join)((0, import_node_os9.homedir)(), ".agenticmail", "agent-memory");
|
|
8273
8448
|
var AgentMemoryStore = class {
|
|
8274
8449
|
dir;
|
|
8275
8450
|
constructor(opts = {}) {
|
|
@@ -8280,10 +8455,10 @@ var AgentMemoryStore = class {
|
|
|
8280
8455
|
}
|
|
8281
8456
|
}
|
|
8282
8457
|
dirFor(agentId) {
|
|
8283
|
-
return (0,
|
|
8458
|
+
return (0, import_node_path11.join)(this.dir, sanitizeId(agentId));
|
|
8284
8459
|
}
|
|
8285
8460
|
pathFor(agentId, threadId) {
|
|
8286
|
-
return (0,
|
|
8461
|
+
return (0, import_node_path11.join)(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
8287
8462
|
}
|
|
8288
8463
|
read(agentId, threadId) {
|
|
8289
8464
|
const p = this.pathFor(agentId, threadId);
|
|
@@ -8385,6 +8560,8 @@ function parse(raw) {
|
|
|
8385
8560
|
InboxWatcher,
|
|
8386
8561
|
MailReceiver,
|
|
8387
8562
|
MailSender,
|
|
8563
|
+
PathTraversalError,
|
|
8564
|
+
REDACTED,
|
|
8388
8565
|
RELAY_PRESETS,
|
|
8389
8566
|
RelayBridge,
|
|
8390
8567
|
RelayGateway,
|
|
@@ -8396,7 +8573,10 @@ function parse(raw) {
|
|
|
8396
8573
|
StalwartAdmin,
|
|
8397
8574
|
ThreadCache,
|
|
8398
8575
|
TunnelManager,
|
|
8576
|
+
UnsafeApiUrlError,
|
|
8399
8577
|
WARNING_THRESHOLD,
|
|
8578
|
+
assertWithinBase,
|
|
8579
|
+
buildApiUrl,
|
|
8400
8580
|
buildInboundSecurityAdvisory,
|
|
8401
8581
|
classifyEmailRoute,
|
|
8402
8582
|
closeDatabase,
|
|
@@ -8415,12 +8595,17 @@ function parse(raw) {
|
|
|
8415
8595
|
parseEmail,
|
|
8416
8596
|
parseGoogleVoiceSms,
|
|
8417
8597
|
recordToolCall,
|
|
8598
|
+
redactObject,
|
|
8599
|
+
redactSecret,
|
|
8418
8600
|
resolveConfig,
|
|
8601
|
+
safeJoin,
|
|
8419
8602
|
sanitizeEmail,
|
|
8420
8603
|
saveConfig,
|
|
8421
8604
|
scanOutboundEmail,
|
|
8422
8605
|
scoreEmail,
|
|
8423
8606
|
setTelemetryVersion,
|
|
8424
8607
|
startRelayBridge,
|
|
8425
|
-
threadIdFor
|
|
8608
|
+
threadIdFor,
|
|
8609
|
+
tryJoin,
|
|
8610
|
+
validateApiUrl
|
|
8426
8611
|
});
|