@agenticmail/api 0.9.14 → 0.9.15
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.js +18 -0
- package/package.json +1 -1
- package/public/js/profile.js +39 -1
- package/public/styles.css +15 -1
package/dist/index.js
CHANGED
|
@@ -557,6 +557,24 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
557
557
|
next(err);
|
|
558
558
|
}
|
|
559
559
|
});
|
|
560
|
+
router.patch("/accounts/:id/host", requireMaster, async (req, res, next) => {
|
|
561
|
+
try {
|
|
562
|
+
const host2 = req.body?.host;
|
|
563
|
+
if (host2 !== null && (typeof host2 !== "string" || !host2.trim())) {
|
|
564
|
+
res.status(400).json({ error: "host must be a non-empty string, or null to unclaim" });
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const patch = host2 === null ? { host: null } : { host: host2.trim() };
|
|
568
|
+
const updated = await accountManager.updateMetadata(req.params.id, patch);
|
|
569
|
+
if (!updated) {
|
|
570
|
+
res.status(404).json({ error: "Agent not found" });
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
res.json(sanitizeAgent(updated));
|
|
574
|
+
} catch (err) {
|
|
575
|
+
next(err);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
560
578
|
router.delete("/accounts/:id", requireMaster, async (req, res, next) => {
|
|
561
579
|
try {
|
|
562
580
|
const allAgents = await accountManager.list();
|
package/package.json
CHANGED
package/public/js/profile.js
CHANGED
|
@@ -27,6 +27,11 @@ export function renderProfile() {
|
|
|
27
27
|
const badge = isBridgeAgent(agent)
|
|
28
28
|
? '<span class="role-badge role-badge-host">Host</span>'
|
|
29
29
|
: '<span class="role-badge role-badge-sub">Sub-agent</span>';
|
|
30
|
+
// Host-ownership badge — shows which LLM the agent rides on.
|
|
31
|
+
// Populated by the MCP server's create_account from
|
|
32
|
+
// AGENTICMAIL_MCP_HOST in the host install's MCP env block.
|
|
33
|
+
// Bridges already show "Host" so we skip the extra chip there.
|
|
34
|
+
const hostTag = !isBridgeAgent(agent) ? hostBadge(agent) : '';
|
|
30
35
|
const check = selected ? `<span class="selected-check">${icon('check', { size: 20 })}</span>` : '';
|
|
31
36
|
const unread = state.unread?.[agent.id] ?? 0;
|
|
32
37
|
const unreadDot = unread > 0
|
|
@@ -36,7 +41,7 @@ export function renderProfile() {
|
|
|
36
41
|
<div class="profile-menu-item" data-id="${agent.id}">
|
|
37
42
|
${avatarHtml(agent, 'avatar-md')}
|
|
38
43
|
<div class="meta">
|
|
39
|
-
<div class="name">${escapeHtml(agent.name)} ${badge} ${unreadDot}</div>
|
|
44
|
+
<div class="name">${escapeHtml(agent.name)} ${badge} ${hostTag} ${unreadDot}</div>
|
|
40
45
|
<div class="email">${escapeHtml(agent.email ?? '')}</div>
|
|
41
46
|
</div>
|
|
42
47
|
${check}
|
|
@@ -45,6 +50,39 @@ export function renderProfile() {
|
|
|
45
50
|
}).join('');
|
|
46
51
|
}
|
|
47
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Render a host-ownership badge for an agent. The host name comes from
|
|
55
|
+
* `metadata.host` on the account. Three states:
|
|
56
|
+
*
|
|
57
|
+
* - "Claude" (purple) — owned by the Claude Code dispatcher
|
|
58
|
+
* - "Codex" (orange) — owned by the OpenAI Codex dispatcher
|
|
59
|
+
* - "Unclaimed" (gray) — no host tag yet; legacy or pre-MCP-tagging.
|
|
60
|
+
* Both dispatchers (if both running) will wake on this account.
|
|
61
|
+
* User can claim with `agenticmail-<host> claim <name>`.
|
|
62
|
+
*
|
|
63
|
+
* Returns an empty string when metadata is genuinely absent and we
|
|
64
|
+
* don't want to clutter the row (e.g. the bridge account itself,
|
|
65
|
+
* which already shows "Host").
|
|
66
|
+
*/
|
|
67
|
+
function hostBadge(agent) {
|
|
68
|
+
const meta = agent.metadata ?? {};
|
|
69
|
+
const host = typeof meta.host === 'string' ? meta.host.toLowerCase() : '';
|
|
70
|
+
if (host === 'claudecode' || host === 'claude') {
|
|
71
|
+
return '<span class="role-badge role-badge-claude" title="Owned by the Claude Code dispatcher (runs on Anthropic via @anthropic-ai/claude-agent-sdk)">Claude</span>';
|
|
72
|
+
}
|
|
73
|
+
if (host === 'codex') {
|
|
74
|
+
return '<span class="role-badge role-badge-codex" title="Owned by the OpenAI Codex dispatcher (runs on OpenAI via @openai/codex-sdk)">Codex</span>';
|
|
75
|
+
}
|
|
76
|
+
if (host) {
|
|
77
|
+
// Unknown host (forward-compat with Grok / Hermes when they land).
|
|
78
|
+
return `<span class="role-badge role-badge-host-other" title="Owned by the ${escapeHtml(host)} dispatcher">${escapeHtml(host)}</span>`;
|
|
79
|
+
}
|
|
80
|
+
// No host tag — surface the "unclaimed" state explicitly so the user
|
|
81
|
+
// notices and runs `agenticmail-<host> claim` if they have multiple
|
|
82
|
+
// dispatchers running.
|
|
83
|
+
return '<span class="role-badge role-badge-unclaimed" title="No host owner — any dispatcher will wake on this account. Run `agenticmail-<host> claim <name>` to settle ownership.">Unclaimed</span>';
|
|
84
|
+
}
|
|
85
|
+
|
|
48
86
|
export function toggleProfileMenu(e) {
|
|
49
87
|
if (e) e.stopPropagation();
|
|
50
88
|
document.getElementById('profile-menu').classList.toggle('open');
|
package/public/styles.css
CHANGED
|
@@ -342,8 +342,22 @@ a { color: var(--accent-strong); }
|
|
|
342
342
|
}
|
|
343
343
|
.role-badge-host { background: #fef3c7; color: #92400e; }
|
|
344
344
|
.role-badge-sub { background: var(--pink-soft); color: var(--accent-strong); }
|
|
345
|
+
/* Host-ownership badges — show which LLM each agent's dispatcher
|
|
346
|
+
rides on. Color-coded by provider so the user can scan an inbox
|
|
347
|
+
list and tell at a glance which agents will wake under which
|
|
348
|
+
model. metadata.host = 'claudecode' → Claude (Anthropic purple);
|
|
349
|
+
'codex' → Codex (OpenAI orange/green); other → generic; absent →
|
|
350
|
+
"Unclaimed" gray with hint to run `agenticmail-<host> claim`. */
|
|
351
|
+
.role-badge-claude { background: #ede9fe; color: #5b21b6; } /* Anthropic purple */
|
|
352
|
+
.role-badge-codex { background: #dcfce7; color: #166534; } /* OpenAI green */
|
|
353
|
+
.role-badge-host-other { background: #dbeafe; color: #1e40af; } /* unknown future host */
|
|
354
|
+
.role-badge-unclaimed { background: #f3f4f6; color: #6b7280; } /* legacy / no owner */
|
|
345
355
|
@media (prefers-color-scheme: dark) {
|
|
346
|
-
.role-badge-host
|
|
356
|
+
.role-badge-host { background: #44290c; color: #fcd34d; }
|
|
357
|
+
.role-badge-claude { background: #3b1f6b; color: #c4b5fd; }
|
|
358
|
+
.role-badge-codex { background: #14361f; color: #86efac; }
|
|
359
|
+
.role-badge-host-other { background: #1e3a8a; color: #93c5fd; }
|
|
360
|
+
.role-badge-unclaimed { background: #1f2937; color: #9ca3af; }
|
|
347
361
|
}
|
|
348
362
|
|
|
349
363
|
/* ─── Main grid: sidebar + content ──────────────────────────────── */
|