Skip to content

Commit 92e5e6c

Browse files
R-M-Naveenclaude
andcommitted
feat(atxp): pass email_user_id during notifications enable
Extract getAccountInfo() as shared export from whoami.ts and use it in webhook.ts to resolve the email local part (e.g. agent_xyz) for cross-system event matching between email-mcp and atxp account IDs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3445df3 commit 92e5e6c

File tree

2 files changed

+49
-30
lines changed

2 files changed

+49
-30
lines changed

packages/atxp/src/commands/webhook.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ function getMachineId(): string | undefined {
151151
return process.env.FLY_MACHINE_ID || os.hostname() || undefined;
152152
}
153153

154+
async function getEmailUserId(): Promise<string | undefined> {
155+
const { getAccountInfo } = await import('./whoami.js');
156+
const account = await getAccountInfo();
157+
if (!account?.email) return undefined;
158+
// Extract local part: agent_xyz@atxp.email -> agent_xyz
159+
return account.email.split('@')[0];
160+
}
161+
154162
async function enableNotifications(): Promise<void> {
155163
const machineId = getMachineId();
156164
if (!machineId) {
@@ -161,10 +169,16 @@ async function enableNotifications(): Promise<void> {
161169

162170
console.log(chalk.gray('Enabling push notifications...'));
163171

172+
// Resolve email user ID for event matching
173+
const emailUserId = await getEmailUserId();
174+
175+
const body: Record<string, string> = { machine_id: machineId };
176+
if (emailUserId) body.email_user_id = emailUserId;
177+
164178
const res = await fetch(`${NOTIFICATIONS_BASE_URL}/notifications/enable`, {
165179
method: 'POST',
166180
headers: { 'Content-Type': 'application/json' },
167-
body: JSON.stringify({ machine_id: machineId }),
181+
body: JSON.stringify(body),
168182
});
169183

170184
const data = await res.json() as Record<string, unknown>;

packages/atxp/src/commands/whoami.ts

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@ function getBaseUrl(connectionString: string): string {
2222
}
2323
}
2424

25+
export interface AccountInfo {
26+
accountId: string;
27+
accountType?: string;
28+
email?: string;
29+
displayName?: string;
30+
sources?: Array<{ chain: string; address: string }>;
31+
team?: { id: string; name: string; role: string };
32+
ownerEmail?: string;
33+
isOrphan?: boolean;
34+
}
35+
36+
export async function getAccountInfo(): Promise<AccountInfo | null> {
37+
const connection = getConnection();
38+
if (!connection) return null;
39+
const token = getConnectionToken(connection);
40+
if (!token) return null;
41+
const baseUrl = getBaseUrl(connection);
42+
try {
43+
const credentials = Buffer.from(`${token}:`).toString('base64');
44+
const response = await fetch(`${baseUrl}/me`, {
45+
headers: { 'Authorization': `Basic ${credentials}` },
46+
});
47+
if (!response.ok) return null;
48+
return await response.json() as AccountInfo;
49+
} catch {
50+
return null;
51+
}
52+
}
53+
2554
export async function whoamiCommand(): Promise<void> {
2655
const connection = getConnection();
2756

@@ -39,45 +68,21 @@ export async function whoamiCommand(): Promise<void> {
3968
process.exit(1);
4069
}
4170

42-
const baseUrl = getBaseUrl(connection);
43-
4471
try {
45-
const credentials = Buffer.from(`${token}:`).toString('base64');
46-
4772
// Fetch account info and phone number in parallel
48-
const [response, phoneNumber] = await Promise.all([
49-
fetch(`${baseUrl}/me`, {
50-
headers: {
51-
'Authorization': `Basic ${credentials}`,
52-
'Content-Type': 'application/json',
53-
},
54-
}),
73+
const [data, phoneNumber] = await Promise.all([
74+
getAccountInfo(),
5575
callTool('phone.mcp.atxp.ai', 'phone_check_sms', {})
5676
.then((r) => { try { return JSON.parse(r).phoneNumber || null; } catch { return null; } })
5777
.catch(() => null),
5878
]);
5979

60-
if (!response.ok) {
61-
if (response.status === 401) {
62-
console.error(chalk.red('Error: Invalid or expired connection token.'));
63-
console.error(`Try logging in again: ${chalk.cyan('npx atxp login --force')}`);
64-
} else {
65-
console.error(chalk.red(`Error: ${response.status} ${response.statusText}`));
66-
}
80+
if (!data) {
81+
console.error(chalk.red('Error: Could not fetch account info.'));
82+
console.error(`Try logging in again: ${chalk.cyan('npx atxp login --force')}`);
6783
process.exit(1);
6884
}
6985

70-
const data = await response.json() as {
71-
accountId: string;
72-
accountType?: string;
73-
email?: string;
74-
displayName?: string;
75-
sources?: Array<{ chain: string; address: string }>;
76-
team?: { id: string; name: string; role: string };
77-
ownerEmail?: string;
78-
isOrphan?: boolean;
79-
};
80-
8186
// Find the primary wallet address from sources
8287
const wallet = data.sources?.[0];
8388

0 commit comments

Comments
 (0)