@agenticmail/core 0.5.58 → 0.5.59
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 +167 -43
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +166 -43
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1918,13 +1918,14 @@ var AccountManager = class {
|
|
|
1918
1918
|
}
|
|
1919
1919
|
const principalName = options.name.toLowerCase();
|
|
1920
1920
|
const email = `${principalName}@${domain}`;
|
|
1921
|
+
const existingAgent = await this.getByName(options.name);
|
|
1922
|
+
if (existingAgent != null) {
|
|
1923
|
+
throw new Error(`Account already exists: ${options.name}`);
|
|
1924
|
+
}
|
|
1921
1925
|
await this.stalwart.ensureDomain(domain);
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
await this.stalwart.deletePrincipal(principalName);
|
|
1926
|
-
} catch {
|
|
1927
|
-
}
|
|
1926
|
+
try {
|
|
1927
|
+
await this.stalwart.deletePrincipal(principalName);
|
|
1928
|
+
} catch {
|
|
1928
1929
|
}
|
|
1929
1930
|
await this.stalwart.createPrincipal({
|
|
1930
1931
|
type: "individual",
|
|
@@ -7076,6 +7077,8 @@ var import_node_child_process4 = require("child_process");
|
|
|
7076
7077
|
var import_node_fs6 = require("fs");
|
|
7077
7078
|
var import_node_path7 = require("path");
|
|
7078
7079
|
var import_node_os6 = require("os");
|
|
7080
|
+
var import_node_module = require("module");
|
|
7081
|
+
var import_meta = {};
|
|
7079
7082
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
7080
7083
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
7081
7084
|
var ServiceManager = class {
|
|
@@ -7102,42 +7105,59 @@ var ServiceManager = class {
|
|
|
7102
7105
|
}
|
|
7103
7106
|
/**
|
|
7104
7107
|
* Find the API server entry point.
|
|
7105
|
-
*
|
|
7108
|
+
*
|
|
7109
|
+
* Issue #26 — Robust path resolution.
|
|
7110
|
+
*
|
|
7111
|
+
* The original implementation hard-coded `node_modules/agenticmail` (the
|
|
7112
|
+
* old unscoped package name). After the rename to `@agenticmail/cli`, that
|
|
7113
|
+
* directory no longer exists, so the resolver fell back to the stale
|
|
7114
|
+
* `~/.agenticmail/api-entry.path` cache and the launchd plist kept pointing
|
|
7115
|
+
* at a deleted path — causing the boot crash loop reported in #26.
|
|
7116
|
+
*
|
|
7117
|
+
* We now prefer `require.resolve('@agenticmail/api')` so the resolution
|
|
7118
|
+
* follows the actual installed location regardless of npm prefix, the
|
|
7119
|
+
* scoped vs unscoped package name, or the package manager (npm global,
|
|
7120
|
+
* pnpm, yarn global, local node_modules). Cached paths are always
|
|
7121
|
+
* validated against the filesystem before being returned.
|
|
7106
7122
|
*/
|
|
7107
7123
|
getApiEntryPath() {
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
} catch {
|
|
7120
|
-
return [];
|
|
7121
|
-
}
|
|
7122
|
-
})(),
|
|
7123
|
-
// Homebrew on macOS
|
|
7124
|
-
"/opt/homebrew/lib/node_modules/agenticmail",
|
|
7125
|
-
"/usr/local/lib/node_modules/agenticmail"
|
|
7124
|
+
try {
|
|
7125
|
+
const req = (0, import_node_module.createRequire)(import_meta.url);
|
|
7126
|
+
const resolved = req.resolve("@agenticmail/api");
|
|
7127
|
+
if ((0, import_node_fs6.existsSync)(resolved)) return resolved;
|
|
7128
|
+
} catch {
|
|
7129
|
+
}
|
|
7130
|
+
const parentPackages = [
|
|
7131
|
+
(0, import_node_path7.join)("@agenticmail", "cli"),
|
|
7132
|
+
// current scoped package
|
|
7133
|
+
"agenticmail"
|
|
7134
|
+
// legacy unscoped package
|
|
7126
7135
|
];
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7136
|
+
const baseDirs = [
|
|
7137
|
+
// user-local install
|
|
7138
|
+
(0, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules")
|
|
7139
|
+
];
|
|
7140
|
+
try {
|
|
7141
|
+
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7142
|
+
baseDirs.push((0, import_node_path7.join)(prefix, "lib", "node_modules"));
|
|
7143
|
+
baseDirs.push((0, import_node_path7.join)(prefix, "node_modules"));
|
|
7144
|
+
} catch {
|
|
7145
|
+
}
|
|
7146
|
+
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
7147
|
+
baseDirs.push("/usr/local/lib/node_modules");
|
|
7148
|
+
for (const base of baseDirs) {
|
|
7149
|
+
const sibling = (0, import_node_path7.join)(base, "@agenticmail", "api", "dist", "index.js");
|
|
7150
|
+
if ((0, import_node_fs6.existsSync)(sibling)) return sibling;
|
|
7151
|
+
for (const parent of parentPackages) {
|
|
7152
|
+
const nested = (0, import_node_path7.join)(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
7153
|
+
if ((0, import_node_fs6.existsSync)(nested)) return nested;
|
|
7134
7154
|
}
|
|
7135
7155
|
}
|
|
7136
7156
|
const dataDir = (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail");
|
|
7137
7157
|
const entryCache = (0, import_node_path7.join)(dataDir, "api-entry.path");
|
|
7138
7158
|
if ((0, import_node_fs6.existsSync)(entryCache)) {
|
|
7139
7159
|
const cached = (0, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
|
|
7140
|
-
if ((0, import_node_fs6.existsSync)(cached)) return cached;
|
|
7160
|
+
if (cached && (0, import_node_fs6.existsSync)(cached)) return cached;
|
|
7141
7161
|
}
|
|
7142
7162
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
7143
7163
|
}
|
|
@@ -7151,25 +7171,49 @@ var ServiceManager = class {
|
|
|
7151
7171
|
}
|
|
7152
7172
|
/**
|
|
7153
7173
|
* Get the current package version.
|
|
7174
|
+
*
|
|
7175
|
+
* Issue #26 — resolve the CLI package.json via require.resolve so the
|
|
7176
|
+
* version reflects the *currently installed* @agenticmail/cli, not a
|
|
7177
|
+
* leftover unscoped `agenticmail` package directory.
|
|
7154
7178
|
*/
|
|
7155
7179
|
getVersion() {
|
|
7156
7180
|
try {
|
|
7157
|
-
const
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7163
|
-
pkgPaths.push((0, import_node_path7.join)(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
7164
|
-
} catch {
|
|
7181
|
+
const req = (0, import_node_module.createRequire)(import_meta.url);
|
|
7182
|
+
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
7183
|
+
if ((0, import_node_fs6.existsSync)(pkgJson)) {
|
|
7184
|
+
const pkg = JSON.parse((0, import_node_fs6.readFileSync)(pkgJson, "utf-8"));
|
|
7185
|
+
if (pkg.version) return pkg.version;
|
|
7165
7186
|
}
|
|
7166
|
-
|
|
7187
|
+
} catch {
|
|
7188
|
+
}
|
|
7189
|
+
try {
|
|
7190
|
+
const apiEntry = this.getApiEntryPath();
|
|
7191
|
+
const apiPkg = (0, import_node_path7.join)(apiEntry, "..", "..", "package.json");
|
|
7192
|
+
if ((0, import_node_fs6.existsSync)(apiPkg)) {
|
|
7193
|
+
const pkg = JSON.parse((0, import_node_fs6.readFileSync)(apiPkg, "utf-8"));
|
|
7194
|
+
if (pkg.version) return pkg.version;
|
|
7195
|
+
}
|
|
7196
|
+
} catch {
|
|
7197
|
+
}
|
|
7198
|
+
const candidates = [
|
|
7199
|
+
(0, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7200
|
+
(0, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules", "agenticmail", "package.json"),
|
|
7201
|
+
(0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "package-version.json")
|
|
7202
|
+
];
|
|
7203
|
+
try {
|
|
7204
|
+
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7205
|
+
candidates.push((0, import_node_path7.join)(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
7206
|
+
candidates.push((0, import_node_path7.join)(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
7207
|
+
} catch {
|
|
7208
|
+
}
|
|
7209
|
+
for (const p of candidates) {
|
|
7210
|
+
try {
|
|
7167
7211
|
if ((0, import_node_fs6.existsSync)(p)) {
|
|
7168
7212
|
const pkg = JSON.parse((0, import_node_fs6.readFileSync)(p, "utf-8"));
|
|
7169
7213
|
if (pkg.version) return pkg.version;
|
|
7170
7214
|
}
|
|
7215
|
+
} catch {
|
|
7171
7216
|
}
|
|
7172
|
-
} catch {
|
|
7173
7217
|
}
|
|
7174
7218
|
return "unknown";
|
|
7175
7219
|
}
|
|
@@ -7475,6 +7519,86 @@ WantedBy=default.target
|
|
|
7475
7519
|
this.uninstall();
|
|
7476
7520
|
return this.install();
|
|
7477
7521
|
}
|
|
7522
|
+
/**
|
|
7523
|
+
* Issue #26 — Detect a stale service installation.
|
|
7524
|
+
*
|
|
7525
|
+
* Background: when a user upgrades from the old unscoped `agenticmail`
|
|
7526
|
+
* package to the new `@agenticmail/cli` scoped package, the old
|
|
7527
|
+
* ~/Library/LaunchAgents/com.agenticmail.server.plist and
|
|
7528
|
+
* ~/.agenticmail/bin/start-server.sh files keep pointing at
|
|
7529
|
+
* /opt/homebrew/lib/node_modules/agenticmail/... — a path that no longer
|
|
7530
|
+
* exists post-rename. The result is a launchd crash loop.
|
|
7531
|
+
*
|
|
7532
|
+
* `needsRepair()` returns a non-null reason whenever:
|
|
7533
|
+
* - the service file exists but the start-server.sh it launches is
|
|
7534
|
+
* missing or references a node_modules path that no longer resolves;
|
|
7535
|
+
* - the embedded service version drifts from the running CLI version
|
|
7536
|
+
* (so service files get refreshed on every upgrade — including
|
|
7537
|
+
* in-place version bumps that don't change the install path);
|
|
7538
|
+
* - the cached API entry path no longer exists on disk.
|
|
7539
|
+
*
|
|
7540
|
+
* Returns null when everything checks out — callers should treat that as
|
|
7541
|
+
* "no action needed".
|
|
7542
|
+
*
|
|
7543
|
+
* Platform-aware: only inspects launchd artefacts on darwin and systemd
|
|
7544
|
+
* artefacts on linux. Returns null on unsupported platforms so this can
|
|
7545
|
+
* be called unconditionally from the CLI's start path.
|
|
7546
|
+
*/
|
|
7547
|
+
needsRepair() {
|
|
7548
|
+
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
7549
|
+
const servicePath = this.getServicePath();
|
|
7550
|
+
if (!(0, import_node_fs6.existsSync)(servicePath)) return null;
|
|
7551
|
+
let serviceContent = "";
|
|
7552
|
+
try {
|
|
7553
|
+
serviceContent = (0, import_node_fs6.readFileSync)(servicePath, "utf-8");
|
|
7554
|
+
} catch {
|
|
7555
|
+
return { reason: "Service file unreadable" };
|
|
7556
|
+
}
|
|
7557
|
+
const startScript = (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin", "start-server.sh");
|
|
7558
|
+
if (serviceContent.includes(startScript)) {
|
|
7559
|
+
if (!(0, import_node_fs6.existsSync)(startScript)) {
|
|
7560
|
+
return { reason: "start-server.sh is missing" };
|
|
7561
|
+
}
|
|
7562
|
+
let scriptContent = "";
|
|
7563
|
+
try {
|
|
7564
|
+
scriptContent = (0, import_node_fs6.readFileSync)(startScript, "utf-8");
|
|
7565
|
+
} catch {
|
|
7566
|
+
return { reason: "start-server.sh unreadable" };
|
|
7567
|
+
}
|
|
7568
|
+
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
7569
|
+
if (apiPathMatch) {
|
|
7570
|
+
const referenced = apiPathMatch[1];
|
|
7571
|
+
if (!(0, import_node_fs6.existsSync)(referenced)) {
|
|
7572
|
+
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
7573
|
+
}
|
|
7574
|
+
}
|
|
7575
|
+
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
7576
|
+
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
7577
|
+
if (stale && !(0, import_node_fs6.existsSync)(stale)) {
|
|
7578
|
+
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
7579
|
+
}
|
|
7580
|
+
}
|
|
7581
|
+
} else {
|
|
7582
|
+
return { reason: "Service file does not reference the wrapper script" };
|
|
7583
|
+
}
|
|
7584
|
+
const currentVersion = this.getVersion();
|
|
7585
|
+
if (currentVersion !== "unknown") {
|
|
7586
|
+
if (!serviceContent.includes(`v${currentVersion}`) || !serviceContent.includes(`AGENTICMAIL_SERVICE_VERSION`) || !serviceContent.includes(currentVersion)) {
|
|
7587
|
+
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
const entryCache = (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "api-entry.path");
|
|
7591
|
+
if ((0, import_node_fs6.existsSync)(entryCache)) {
|
|
7592
|
+
try {
|
|
7593
|
+
const cached = (0, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
|
|
7594
|
+
if (cached && !(0, import_node_fs6.existsSync)(cached)) {
|
|
7595
|
+
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
7596
|
+
}
|
|
7597
|
+
} catch {
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
return null;
|
|
7601
|
+
}
|
|
7478
7602
|
};
|
|
7479
7603
|
|
|
7480
7604
|
// src/setup/index.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -1558,7 +1558,20 @@ declare class ServiceManager {
|
|
|
1558
1558
|
private getNodePath;
|
|
1559
1559
|
/**
|
|
1560
1560
|
* Find the API server entry point.
|
|
1561
|
-
*
|
|
1561
|
+
*
|
|
1562
|
+
* Issue #26 — Robust path resolution.
|
|
1563
|
+
*
|
|
1564
|
+
* The original implementation hard-coded `node_modules/agenticmail` (the
|
|
1565
|
+
* old unscoped package name). After the rename to `@agenticmail/cli`, that
|
|
1566
|
+
* directory no longer exists, so the resolver fell back to the stale
|
|
1567
|
+
* `~/.agenticmail/api-entry.path` cache and the launchd plist kept pointing
|
|
1568
|
+
* at a deleted path — causing the boot crash loop reported in #26.
|
|
1569
|
+
*
|
|
1570
|
+
* We now prefer `require.resolve('@agenticmail/api')` so the resolution
|
|
1571
|
+
* follows the actual installed location regardless of npm prefix, the
|
|
1572
|
+
* scoped vs unscoped package name, or the package manager (npm global,
|
|
1573
|
+
* pnpm, yarn global, local node_modules). Cached paths are always
|
|
1574
|
+
* validated against the filesystem before being returned.
|
|
1562
1575
|
*/
|
|
1563
1576
|
private getApiEntryPath;
|
|
1564
1577
|
/**
|
|
@@ -1567,6 +1580,10 @@ declare class ServiceManager {
|
|
|
1567
1580
|
cacheApiEntryPath(entryPath: string): void;
|
|
1568
1581
|
/**
|
|
1569
1582
|
* Get the current package version.
|
|
1583
|
+
*
|
|
1584
|
+
* Issue #26 — resolve the CLI package.json via require.resolve so the
|
|
1585
|
+
* version reflects the *currently installed* @agenticmail/cli, not a
|
|
1586
|
+
* leftover unscoped `agenticmail` package directory.
|
|
1570
1587
|
*/
|
|
1571
1588
|
private getVersion;
|
|
1572
1589
|
/**
|
|
@@ -1619,6 +1636,34 @@ declare class ServiceManager {
|
|
|
1619
1636
|
installed: boolean;
|
|
1620
1637
|
message: string;
|
|
1621
1638
|
};
|
|
1639
|
+
/**
|
|
1640
|
+
* Issue #26 — Detect a stale service installation.
|
|
1641
|
+
*
|
|
1642
|
+
* Background: when a user upgrades from the old unscoped `agenticmail`
|
|
1643
|
+
* package to the new `@agenticmail/cli` scoped package, the old
|
|
1644
|
+
* ~/Library/LaunchAgents/com.agenticmail.server.plist and
|
|
1645
|
+
* ~/.agenticmail/bin/start-server.sh files keep pointing at
|
|
1646
|
+
* /opt/homebrew/lib/node_modules/agenticmail/... — a path that no longer
|
|
1647
|
+
* exists post-rename. The result is a launchd crash loop.
|
|
1648
|
+
*
|
|
1649
|
+
* `needsRepair()` returns a non-null reason whenever:
|
|
1650
|
+
* - the service file exists but the start-server.sh it launches is
|
|
1651
|
+
* missing or references a node_modules path that no longer resolves;
|
|
1652
|
+
* - the embedded service version drifts from the running CLI version
|
|
1653
|
+
* (so service files get refreshed on every upgrade — including
|
|
1654
|
+
* in-place version bumps that don't change the install path);
|
|
1655
|
+
* - the cached API entry path no longer exists on disk.
|
|
1656
|
+
*
|
|
1657
|
+
* Returns null when everything checks out — callers should treat that as
|
|
1658
|
+
* "no action needed".
|
|
1659
|
+
*
|
|
1660
|
+
* Platform-aware: only inspects launchd artefacts on darwin and systemd
|
|
1661
|
+
* artefacts on linux. Returns null on unsupported platforms so this can
|
|
1662
|
+
* be called unconditionally from the CLI's start path.
|
|
1663
|
+
*/
|
|
1664
|
+
needsRepair(): {
|
|
1665
|
+
reason: string;
|
|
1666
|
+
} | null;
|
|
1622
1667
|
}
|
|
1623
1668
|
|
|
1624
1669
|
interface SetupConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -1558,7 +1558,20 @@ declare class ServiceManager {
|
|
|
1558
1558
|
private getNodePath;
|
|
1559
1559
|
/**
|
|
1560
1560
|
* Find the API server entry point.
|
|
1561
|
-
*
|
|
1561
|
+
*
|
|
1562
|
+
* Issue #26 — Robust path resolution.
|
|
1563
|
+
*
|
|
1564
|
+
* The original implementation hard-coded `node_modules/agenticmail` (the
|
|
1565
|
+
* old unscoped package name). After the rename to `@agenticmail/cli`, that
|
|
1566
|
+
* directory no longer exists, so the resolver fell back to the stale
|
|
1567
|
+
* `~/.agenticmail/api-entry.path` cache and the launchd plist kept pointing
|
|
1568
|
+
* at a deleted path — causing the boot crash loop reported in #26.
|
|
1569
|
+
*
|
|
1570
|
+
* We now prefer `require.resolve('@agenticmail/api')` so the resolution
|
|
1571
|
+
* follows the actual installed location regardless of npm prefix, the
|
|
1572
|
+
* scoped vs unscoped package name, or the package manager (npm global,
|
|
1573
|
+
* pnpm, yarn global, local node_modules). Cached paths are always
|
|
1574
|
+
* validated against the filesystem before being returned.
|
|
1562
1575
|
*/
|
|
1563
1576
|
private getApiEntryPath;
|
|
1564
1577
|
/**
|
|
@@ -1567,6 +1580,10 @@ declare class ServiceManager {
|
|
|
1567
1580
|
cacheApiEntryPath(entryPath: string): void;
|
|
1568
1581
|
/**
|
|
1569
1582
|
* Get the current package version.
|
|
1583
|
+
*
|
|
1584
|
+
* Issue #26 — resolve the CLI package.json via require.resolve so the
|
|
1585
|
+
* version reflects the *currently installed* @agenticmail/cli, not a
|
|
1586
|
+
* leftover unscoped `agenticmail` package directory.
|
|
1570
1587
|
*/
|
|
1571
1588
|
private getVersion;
|
|
1572
1589
|
/**
|
|
@@ -1619,6 +1636,34 @@ declare class ServiceManager {
|
|
|
1619
1636
|
installed: boolean;
|
|
1620
1637
|
message: string;
|
|
1621
1638
|
};
|
|
1639
|
+
/**
|
|
1640
|
+
* Issue #26 — Detect a stale service installation.
|
|
1641
|
+
*
|
|
1642
|
+
* Background: when a user upgrades from the old unscoped `agenticmail`
|
|
1643
|
+
* package to the new `@agenticmail/cli` scoped package, the old
|
|
1644
|
+
* ~/Library/LaunchAgents/com.agenticmail.server.plist and
|
|
1645
|
+
* ~/.agenticmail/bin/start-server.sh files keep pointing at
|
|
1646
|
+
* /opt/homebrew/lib/node_modules/agenticmail/... — a path that no longer
|
|
1647
|
+
* exists post-rename. The result is a launchd crash loop.
|
|
1648
|
+
*
|
|
1649
|
+
* `needsRepair()` returns a non-null reason whenever:
|
|
1650
|
+
* - the service file exists but the start-server.sh it launches is
|
|
1651
|
+
* missing or references a node_modules path that no longer resolves;
|
|
1652
|
+
* - the embedded service version drifts from the running CLI version
|
|
1653
|
+
* (so service files get refreshed on every upgrade — including
|
|
1654
|
+
* in-place version bumps that don't change the install path);
|
|
1655
|
+
* - the cached API entry path no longer exists on disk.
|
|
1656
|
+
*
|
|
1657
|
+
* Returns null when everything checks out — callers should treat that as
|
|
1658
|
+
* "no action needed".
|
|
1659
|
+
*
|
|
1660
|
+
* Platform-aware: only inspects launchd artefacts on darwin and systemd
|
|
1661
|
+
* artefacts on linux. Returns null on unsupported platforms so this can
|
|
1662
|
+
* be called unconditionally from the CLI's start path.
|
|
1663
|
+
*/
|
|
1664
|
+
needsRepair(): {
|
|
1665
|
+
reason: string;
|
|
1666
|
+
} | null;
|
|
1622
1667
|
}
|
|
1623
1668
|
|
|
1624
1669
|
interface SetupConfig {
|
package/dist/index.js
CHANGED
|
@@ -1164,13 +1164,14 @@ var AccountManager = class {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
const principalName = options.name.toLowerCase();
|
|
1166
1166
|
const email = `${principalName}@${domain}`;
|
|
1167
|
+
const existingAgent = await this.getByName(options.name);
|
|
1168
|
+
if (existingAgent != null) {
|
|
1169
|
+
throw new Error(`Account already exists: ${options.name}`);
|
|
1170
|
+
}
|
|
1167
1171
|
await this.stalwart.ensureDomain(domain);
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
await this.stalwart.deletePrincipal(principalName);
|
|
1172
|
-
} catch {
|
|
1173
|
-
}
|
|
1172
|
+
try {
|
|
1173
|
+
await this.stalwart.deletePrincipal(principalName);
|
|
1174
|
+
} catch {
|
|
1174
1175
|
}
|
|
1175
1176
|
await this.stalwart.createPrincipal({
|
|
1176
1177
|
type: "individual",
|
|
@@ -6318,6 +6319,7 @@ import { execFileSync as execFileSync3, execSync as execSync2 } from "child_proc
|
|
|
6318
6319
|
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
6319
6320
|
import { join as join8 } from "path";
|
|
6320
6321
|
import { homedir as homedir7, platform as platform4 } from "os";
|
|
6322
|
+
import { createRequire } from "module";
|
|
6321
6323
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
6322
6324
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
6323
6325
|
var ServiceManager = class {
|
|
@@ -6344,42 +6346,59 @@ var ServiceManager = class {
|
|
|
6344
6346
|
}
|
|
6345
6347
|
/**
|
|
6346
6348
|
* Find the API server entry point.
|
|
6347
|
-
*
|
|
6349
|
+
*
|
|
6350
|
+
* Issue #26 — Robust path resolution.
|
|
6351
|
+
*
|
|
6352
|
+
* The original implementation hard-coded `node_modules/agenticmail` (the
|
|
6353
|
+
* old unscoped package name). After the rename to `@agenticmail/cli`, that
|
|
6354
|
+
* directory no longer exists, so the resolver fell back to the stale
|
|
6355
|
+
* `~/.agenticmail/api-entry.path` cache and the launchd plist kept pointing
|
|
6356
|
+
* at a deleted path — causing the boot crash loop reported in #26.
|
|
6357
|
+
*
|
|
6358
|
+
* We now prefer `require.resolve('@agenticmail/api')` so the resolution
|
|
6359
|
+
* follows the actual installed location regardless of npm prefix, the
|
|
6360
|
+
* scoped vs unscoped package name, or the package manager (npm global,
|
|
6361
|
+
* pnpm, yarn global, local node_modules). Cached paths are always
|
|
6362
|
+
* validated against the filesystem before being returned.
|
|
6348
6363
|
*/
|
|
6349
6364
|
getApiEntryPath() {
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
} catch {
|
|
6362
|
-
return [];
|
|
6363
|
-
}
|
|
6364
|
-
})(),
|
|
6365
|
-
// Homebrew on macOS
|
|
6366
|
-
"/opt/homebrew/lib/node_modules/agenticmail",
|
|
6367
|
-
"/usr/local/lib/node_modules/agenticmail"
|
|
6365
|
+
try {
|
|
6366
|
+
const req = createRequire(import.meta.url);
|
|
6367
|
+
const resolved = req.resolve("@agenticmail/api");
|
|
6368
|
+
if (existsSync6(resolved)) return resolved;
|
|
6369
|
+
} catch {
|
|
6370
|
+
}
|
|
6371
|
+
const parentPackages = [
|
|
6372
|
+
join8("@agenticmail", "cli"),
|
|
6373
|
+
// current scoped package
|
|
6374
|
+
"agenticmail"
|
|
6375
|
+
// legacy unscoped package
|
|
6368
6376
|
];
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6377
|
+
const baseDirs = [
|
|
6378
|
+
// user-local install
|
|
6379
|
+
join8(homedir7(), "node_modules")
|
|
6380
|
+
];
|
|
6381
|
+
try {
|
|
6382
|
+
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6383
|
+
baseDirs.push(join8(prefix, "lib", "node_modules"));
|
|
6384
|
+
baseDirs.push(join8(prefix, "node_modules"));
|
|
6385
|
+
} catch {
|
|
6386
|
+
}
|
|
6387
|
+
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
6388
|
+
baseDirs.push("/usr/local/lib/node_modules");
|
|
6389
|
+
for (const base of baseDirs) {
|
|
6390
|
+
const sibling = join8(base, "@agenticmail", "api", "dist", "index.js");
|
|
6391
|
+
if (existsSync6(sibling)) return sibling;
|
|
6392
|
+
for (const parent of parentPackages) {
|
|
6393
|
+
const nested = join8(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
6394
|
+
if (existsSync6(nested)) return nested;
|
|
6376
6395
|
}
|
|
6377
6396
|
}
|
|
6378
6397
|
const dataDir = join8(homedir7(), ".agenticmail");
|
|
6379
6398
|
const entryCache = join8(dataDir, "api-entry.path");
|
|
6380
6399
|
if (existsSync6(entryCache)) {
|
|
6381
6400
|
const cached = readFileSync3(entryCache, "utf-8").trim();
|
|
6382
|
-
if (existsSync6(cached)) return cached;
|
|
6401
|
+
if (cached && existsSync6(cached)) return cached;
|
|
6383
6402
|
}
|
|
6384
6403
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
6385
6404
|
}
|
|
@@ -6393,25 +6412,49 @@ var ServiceManager = class {
|
|
|
6393
6412
|
}
|
|
6394
6413
|
/**
|
|
6395
6414
|
* Get the current package version.
|
|
6415
|
+
*
|
|
6416
|
+
* Issue #26 — resolve the CLI package.json via require.resolve so the
|
|
6417
|
+
* version reflects the *currently installed* @agenticmail/cli, not a
|
|
6418
|
+
* leftover unscoped `agenticmail` package directory.
|
|
6396
6419
|
*/
|
|
6397
6420
|
getVersion() {
|
|
6398
6421
|
try {
|
|
6399
|
-
const
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6405
|
-
pkgPaths.push(join8(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
6406
|
-
} catch {
|
|
6422
|
+
const req = createRequire(import.meta.url);
|
|
6423
|
+
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
6424
|
+
if (existsSync6(pkgJson)) {
|
|
6425
|
+
const pkg = JSON.parse(readFileSync3(pkgJson, "utf-8"));
|
|
6426
|
+
if (pkg.version) return pkg.version;
|
|
6407
6427
|
}
|
|
6408
|
-
|
|
6428
|
+
} catch {
|
|
6429
|
+
}
|
|
6430
|
+
try {
|
|
6431
|
+
const apiEntry = this.getApiEntryPath();
|
|
6432
|
+
const apiPkg = join8(apiEntry, "..", "..", "package.json");
|
|
6433
|
+
if (existsSync6(apiPkg)) {
|
|
6434
|
+
const pkg = JSON.parse(readFileSync3(apiPkg, "utf-8"));
|
|
6435
|
+
if (pkg.version) return pkg.version;
|
|
6436
|
+
}
|
|
6437
|
+
} catch {
|
|
6438
|
+
}
|
|
6439
|
+
const candidates = [
|
|
6440
|
+
join8(homedir7(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
6441
|
+
join8(homedir7(), "node_modules", "agenticmail", "package.json"),
|
|
6442
|
+
join8(homedir7(), ".agenticmail", "package-version.json")
|
|
6443
|
+
];
|
|
6444
|
+
try {
|
|
6445
|
+
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6446
|
+
candidates.push(join8(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
6447
|
+
candidates.push(join8(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
6448
|
+
} catch {
|
|
6449
|
+
}
|
|
6450
|
+
for (const p of candidates) {
|
|
6451
|
+
try {
|
|
6409
6452
|
if (existsSync6(p)) {
|
|
6410
6453
|
const pkg = JSON.parse(readFileSync3(p, "utf-8"));
|
|
6411
6454
|
if (pkg.version) return pkg.version;
|
|
6412
6455
|
}
|
|
6456
|
+
} catch {
|
|
6413
6457
|
}
|
|
6414
|
-
} catch {
|
|
6415
6458
|
}
|
|
6416
6459
|
return "unknown";
|
|
6417
6460
|
}
|
|
@@ -6717,6 +6760,86 @@ WantedBy=default.target
|
|
|
6717
6760
|
this.uninstall();
|
|
6718
6761
|
return this.install();
|
|
6719
6762
|
}
|
|
6763
|
+
/**
|
|
6764
|
+
* Issue #26 — Detect a stale service installation.
|
|
6765
|
+
*
|
|
6766
|
+
* Background: when a user upgrades from the old unscoped `agenticmail`
|
|
6767
|
+
* package to the new `@agenticmail/cli` scoped package, the old
|
|
6768
|
+
* ~/Library/LaunchAgents/com.agenticmail.server.plist and
|
|
6769
|
+
* ~/.agenticmail/bin/start-server.sh files keep pointing at
|
|
6770
|
+
* /opt/homebrew/lib/node_modules/agenticmail/... — a path that no longer
|
|
6771
|
+
* exists post-rename. The result is a launchd crash loop.
|
|
6772
|
+
*
|
|
6773
|
+
* `needsRepair()` returns a non-null reason whenever:
|
|
6774
|
+
* - the service file exists but the start-server.sh it launches is
|
|
6775
|
+
* missing or references a node_modules path that no longer resolves;
|
|
6776
|
+
* - the embedded service version drifts from the running CLI version
|
|
6777
|
+
* (so service files get refreshed on every upgrade — including
|
|
6778
|
+
* in-place version bumps that don't change the install path);
|
|
6779
|
+
* - the cached API entry path no longer exists on disk.
|
|
6780
|
+
*
|
|
6781
|
+
* Returns null when everything checks out — callers should treat that as
|
|
6782
|
+
* "no action needed".
|
|
6783
|
+
*
|
|
6784
|
+
* Platform-aware: only inspects launchd artefacts on darwin and systemd
|
|
6785
|
+
* artefacts on linux. Returns null on unsupported platforms so this can
|
|
6786
|
+
* be called unconditionally from the CLI's start path.
|
|
6787
|
+
*/
|
|
6788
|
+
needsRepair() {
|
|
6789
|
+
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
6790
|
+
const servicePath = this.getServicePath();
|
|
6791
|
+
if (!existsSync6(servicePath)) return null;
|
|
6792
|
+
let serviceContent = "";
|
|
6793
|
+
try {
|
|
6794
|
+
serviceContent = readFileSync3(servicePath, "utf-8");
|
|
6795
|
+
} catch {
|
|
6796
|
+
return { reason: "Service file unreadable" };
|
|
6797
|
+
}
|
|
6798
|
+
const startScript = join8(homedir7(), ".agenticmail", "bin", "start-server.sh");
|
|
6799
|
+
if (serviceContent.includes(startScript)) {
|
|
6800
|
+
if (!existsSync6(startScript)) {
|
|
6801
|
+
return { reason: "start-server.sh is missing" };
|
|
6802
|
+
}
|
|
6803
|
+
let scriptContent = "";
|
|
6804
|
+
try {
|
|
6805
|
+
scriptContent = readFileSync3(startScript, "utf-8");
|
|
6806
|
+
} catch {
|
|
6807
|
+
return { reason: "start-server.sh unreadable" };
|
|
6808
|
+
}
|
|
6809
|
+
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
6810
|
+
if (apiPathMatch) {
|
|
6811
|
+
const referenced = apiPathMatch[1];
|
|
6812
|
+
if (!existsSync6(referenced)) {
|
|
6813
|
+
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
6817
|
+
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
6818
|
+
if (stale && !existsSync6(stale)) {
|
|
6819
|
+
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
} else {
|
|
6823
|
+
return { reason: "Service file does not reference the wrapper script" };
|
|
6824
|
+
}
|
|
6825
|
+
const currentVersion = this.getVersion();
|
|
6826
|
+
if (currentVersion !== "unknown") {
|
|
6827
|
+
if (!serviceContent.includes(`v${currentVersion}`) || !serviceContent.includes(`AGENTICMAIL_SERVICE_VERSION`) || !serviceContent.includes(currentVersion)) {
|
|
6828
|
+
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
const entryCache = join8(homedir7(), ".agenticmail", "api-entry.path");
|
|
6832
|
+
if (existsSync6(entryCache)) {
|
|
6833
|
+
try {
|
|
6834
|
+
const cached = readFileSync3(entryCache, "utf-8").trim();
|
|
6835
|
+
if (cached && !existsSync6(cached)) {
|
|
6836
|
+
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
6837
|
+
}
|
|
6838
|
+
} catch {
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
return null;
|
|
6842
|
+
}
|
|
6720
6843
|
};
|
|
6721
6844
|
|
|
6722
6845
|
// src/setup/index.ts
|