aamp-sdk 0.1.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/README.md +43 -0
- package/dist/client.d.ts +91 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +196 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/jmap-push.d.ts +104 -0
- package/dist/jmap-push.d.ts.map +1 -0
- package/dist/jmap-push.js +634 -0
- package/dist/jmap-push.js.map +1 -0
- package/dist/parser.d.ts +65 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +187 -0
- package/dist/parser.js.map +1 -0
- package/dist/smtp-sender.d.ts +57 -0
- package/dist/smtp-sender.d.ts.map +1 -0
- package/dist/smtp-sender.js +282 -0
- package/dist/smtp-sender.js.map +1 -0
- package/dist/types.d.ts +168 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @byted-meego/aamp-sdk
|
|
2
|
+
|
|
3
|
+
Node.js SDK for connecting agents and services to AAMP.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @byted-meego/aamp-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { AampClient } from '@byted-meego/aamp-sdk'
|
|
15
|
+
|
|
16
|
+
const client = new AampClient({
|
|
17
|
+
email: 'agent@example.com',
|
|
18
|
+
jmapToken: '<base64(email:password)>',
|
|
19
|
+
jmapUrl: 'http://localhost:3000/jmap',
|
|
20
|
+
smtpHost: 'localhost',
|
|
21
|
+
smtpPort: 587,
|
|
22
|
+
smtpPassword: '<smtp-password>',
|
|
23
|
+
rejectUnauthorized: false,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
client.on('task.dispatch', async (task) => {
|
|
27
|
+
await client.sendResult({
|
|
28
|
+
to: task.from,
|
|
29
|
+
taskId: task.taskId,
|
|
30
|
+
status: 'completed',
|
|
31
|
+
output: 'done',
|
|
32
|
+
inReplyTo: task.messageId,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
await client.connect()
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Exports
|
|
40
|
+
|
|
41
|
+
- `AampClient`
|
|
42
|
+
- `JmapPushClient`
|
|
43
|
+
- protocol types such as `TaskDispatch`, `TaskResult`, and `TaskHelp`
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AampClient — Main SDK entry point
|
|
3
|
+
*
|
|
4
|
+
* Combines JMAP WebSocket Push (receive) + SMTP (send) into a single client.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
*
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const client = new AampClient({
|
|
10
|
+
* email: 'codereviewer-abc@aamp.example.com',
|
|
11
|
+
* jmapToken: '<base64-token>',
|
|
12
|
+
* jmapUrl: 'http://localhost:8080',
|
|
13
|
+
* smtpHost: 'localhost',
|
|
14
|
+
* smtpPort: 587,
|
|
15
|
+
* smtpPassword: 'agent-smtp-password',
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Listen for incoming tasks
|
|
19
|
+
* client.on('task.dispatch', async (task) => {
|
|
20
|
+
* const result = await doWork(task)
|
|
21
|
+
* await client.sendResult({
|
|
22
|
+
* to: task.from,
|
|
23
|
+
* taskId: task.taskId,
|
|
24
|
+
* status: 'completed',
|
|
25
|
+
* output: result,
|
|
26
|
+
* })
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* await client.connect()
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import { EventEmitter } from 'events';
|
|
33
|
+
import type { AampClientConfig, AampClientEvents, SendTaskOptions, SendResultOptions, SendHelpOptions } from './types.js';
|
|
34
|
+
export declare class AampClient extends EventEmitter {
|
|
35
|
+
private jmapClient;
|
|
36
|
+
private smtpSender;
|
|
37
|
+
private readonly config;
|
|
38
|
+
constructor(config: AampClientConfig);
|
|
39
|
+
on<K extends keyof AampClientEvents>(event: K, listener: AampClientEvents[K]): this;
|
|
40
|
+
once<K extends keyof AampClientEvents>(event: K, listener: AampClientEvents[K]): this;
|
|
41
|
+
off<K extends keyof AampClientEvents>(event: K, listener: AampClientEvents[K]): this;
|
|
42
|
+
/**
|
|
43
|
+
* Connect to JMAP and start listening for tasks
|
|
44
|
+
*/
|
|
45
|
+
connect(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Disconnect and clean up
|
|
48
|
+
*/
|
|
49
|
+
disconnect(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Returns true if the JMAP connection is active
|
|
52
|
+
*/
|
|
53
|
+
isConnected(): boolean;
|
|
54
|
+
isUsingPollingFallback(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Send a task.dispatch email to an agent.
|
|
57
|
+
* Returns the generated taskId and the SMTP Message-ID.
|
|
58
|
+
* Store messageId → taskId in Redis/DB to support In-Reply-To thread routing
|
|
59
|
+
* for human replies that arrive without X-AAMP headers.
|
|
60
|
+
*/
|
|
61
|
+
sendTask(opts: SendTaskOptions): Promise<{
|
|
62
|
+
taskId: string;
|
|
63
|
+
messageId: string;
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Send a task.result email (agent → system/dispatcher)
|
|
67
|
+
*/
|
|
68
|
+
sendResult(opts: SendResultOptions): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Send a task.help email when the agent needs human assistance
|
|
71
|
+
*/
|
|
72
|
+
sendHelp(opts: SendHelpOptions): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Download a blob (attachment) by its JMAP blobId.
|
|
75
|
+
* Use this to retrieve attachment content from received TaskDispatch or TaskResult messages.
|
|
76
|
+
* Returns the raw binary content as a Buffer.
|
|
77
|
+
*/
|
|
78
|
+
downloadBlob(blobId: string, filename?: string): Promise<Buffer>;
|
|
79
|
+
/**
|
|
80
|
+
* Reconcile recent mailbox contents via JMAP HTTP to catch messages missed by
|
|
81
|
+
* a flaky WebSocket path. Safe to call periodically; duplicate processing is
|
|
82
|
+
* suppressed by the JMAP push client.
|
|
83
|
+
*/
|
|
84
|
+
reconcileRecentEmails(limit?: number): Promise<number>;
|
|
85
|
+
/**
|
|
86
|
+
* Verify SMTP connectivity
|
|
87
|
+
*/
|
|
88
|
+
verifySmtp(): Promise<boolean>;
|
|
89
|
+
get email(): string;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAGrC,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAMhB,eAAe,EACf,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,MAAM,EAAE,gBAAgB;IAkF3B,EAAE,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC1C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,IAAI;IAIE,IAAI,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC5C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,IAAI;IAIE,GAAG,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC3C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,IAAI;IAQP;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB,sBAAsB,IAAI,OAAO;IAQjC;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAIrF;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD;;OAEG;IACG,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD;;;;OAIG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItE;;;;OAIG;IACG,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAIpC,IAAI,KAAK,IAAI,MAAM,CAElB;CACF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AampClient — Main SDK entry point
|
|
3
|
+
*
|
|
4
|
+
* Combines JMAP WebSocket Push (receive) + SMTP (send) into a single client.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
*
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const client = new AampClient({
|
|
10
|
+
* email: 'codereviewer-abc@aamp.example.com',
|
|
11
|
+
* jmapToken: '<base64-token>',
|
|
12
|
+
* jmapUrl: 'http://localhost:8080',
|
|
13
|
+
* smtpHost: 'localhost',
|
|
14
|
+
* smtpPort: 587,
|
|
15
|
+
* smtpPassword: 'agent-smtp-password',
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Listen for incoming tasks
|
|
19
|
+
* client.on('task.dispatch', async (task) => {
|
|
20
|
+
* const result = await doWork(task)
|
|
21
|
+
* await client.sendResult({
|
|
22
|
+
* to: task.from,
|
|
23
|
+
* taskId: task.taskId,
|
|
24
|
+
* status: 'completed',
|
|
25
|
+
* output: result,
|
|
26
|
+
* })
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* await client.connect()
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import { EventEmitter } from 'events';
|
|
33
|
+
import { JmapPushClient } from './jmap-push.js';
|
|
34
|
+
import { SmtpSender } from './smtp-sender.js';
|
|
35
|
+
export class AampClient extends EventEmitter {
|
|
36
|
+
jmapClient;
|
|
37
|
+
smtpSender;
|
|
38
|
+
config;
|
|
39
|
+
constructor(config) {
|
|
40
|
+
super();
|
|
41
|
+
this.config = config;
|
|
42
|
+
// Decode JMAP token (format: base64(email:password))
|
|
43
|
+
let password;
|
|
44
|
+
try {
|
|
45
|
+
const decoded = Buffer.from(config.jmapToken, 'base64').toString('utf-8');
|
|
46
|
+
const colonIdx = decoded.indexOf(':');
|
|
47
|
+
if (colonIdx < 0)
|
|
48
|
+
throw new Error('Invalid jmapToken format: expected base64(email:password)');
|
|
49
|
+
password = decoded.slice(colonIdx + 1);
|
|
50
|
+
if (!password)
|
|
51
|
+
throw new Error('Invalid jmapToken: empty password');
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof Error && err.message.startsWith('Invalid jmapToken'))
|
|
55
|
+
throw err;
|
|
56
|
+
throw new Error(`Failed to decode jmapToken: ${err.message}`);
|
|
57
|
+
}
|
|
58
|
+
this.jmapClient = new JmapPushClient({
|
|
59
|
+
email: config.email,
|
|
60
|
+
password: password ?? config.smtpPassword,
|
|
61
|
+
jmapUrl: config.jmapUrl,
|
|
62
|
+
reconnectInterval: config.reconnectInterval ?? 5000,
|
|
63
|
+
rejectUnauthorized: config.rejectUnauthorized,
|
|
64
|
+
});
|
|
65
|
+
this.smtpSender = new SmtpSender({
|
|
66
|
+
host: config.smtpHost,
|
|
67
|
+
port: config.smtpPort ?? 587,
|
|
68
|
+
user: config.email,
|
|
69
|
+
password: config.smtpPassword,
|
|
70
|
+
httpBaseUrl: config.httpSendBaseUrl ?? config.jmapUrl,
|
|
71
|
+
authToken: config.jmapToken,
|
|
72
|
+
rejectUnauthorized: config.rejectUnauthorized,
|
|
73
|
+
});
|
|
74
|
+
// Forward JMAP events to this emitter
|
|
75
|
+
this.jmapClient.on('task.dispatch', (task) => {
|
|
76
|
+
this.emit('task.dispatch', task);
|
|
77
|
+
});
|
|
78
|
+
this.jmapClient.on('task.result', (result) => {
|
|
79
|
+
this.emit('task.result', result);
|
|
80
|
+
});
|
|
81
|
+
this.jmapClient.on('task.help', (help) => {
|
|
82
|
+
this.emit('task.help', help);
|
|
83
|
+
});
|
|
84
|
+
this.jmapClient.on('task.ack', (ack) => {
|
|
85
|
+
this.emit('task.ack', ack);
|
|
86
|
+
});
|
|
87
|
+
// Auto-ACK: when a task.dispatch is received, automatically send an ACK back
|
|
88
|
+
this.jmapClient.on('_autoAck', async ({ to, taskId, messageId }) => {
|
|
89
|
+
try {
|
|
90
|
+
await this.smtpSender.sendAck({ to, taskId, inReplyTo: messageId });
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.warn(`[AAMP] Failed to send ACK for task ${taskId}: ${err.message}`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
this.jmapClient.on('reply', (reply) => {
|
|
97
|
+
this.emit('reply', reply);
|
|
98
|
+
});
|
|
99
|
+
this.jmapClient.on('connected', () => {
|
|
100
|
+
this.emit('connected');
|
|
101
|
+
});
|
|
102
|
+
this.jmapClient.on('disconnected', (reason) => {
|
|
103
|
+
this.emit('disconnected', reason);
|
|
104
|
+
});
|
|
105
|
+
this.jmapClient.on('error', (err) => {
|
|
106
|
+
this.emit('error', err);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// =====================================================
|
|
110
|
+
// Type-safe event emitter methods
|
|
111
|
+
// =====================================================
|
|
112
|
+
on(event, listener) {
|
|
113
|
+
return super.on(event, listener);
|
|
114
|
+
}
|
|
115
|
+
once(event, listener) {
|
|
116
|
+
return super.once(event, listener);
|
|
117
|
+
}
|
|
118
|
+
off(event, listener) {
|
|
119
|
+
return super.off(event, listener);
|
|
120
|
+
}
|
|
121
|
+
// =====================================================
|
|
122
|
+
// Lifecycle
|
|
123
|
+
// =====================================================
|
|
124
|
+
/**
|
|
125
|
+
* Connect to JMAP and start listening for tasks
|
|
126
|
+
*/
|
|
127
|
+
async connect() {
|
|
128
|
+
await this.jmapClient.start();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Disconnect and clean up
|
|
132
|
+
*/
|
|
133
|
+
disconnect() {
|
|
134
|
+
this.jmapClient.stop();
|
|
135
|
+
this.smtpSender.close();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns true if the JMAP connection is active
|
|
139
|
+
*/
|
|
140
|
+
isConnected() {
|
|
141
|
+
return this.jmapClient.isConnected();
|
|
142
|
+
}
|
|
143
|
+
isUsingPollingFallback() {
|
|
144
|
+
return this.jmapClient.isUsingPollingFallback();
|
|
145
|
+
}
|
|
146
|
+
// =====================================================
|
|
147
|
+
// Sending
|
|
148
|
+
// =====================================================
|
|
149
|
+
/**
|
|
150
|
+
* Send a task.dispatch email to an agent.
|
|
151
|
+
* Returns the generated taskId and the SMTP Message-ID.
|
|
152
|
+
* Store messageId → taskId in Redis/DB to support In-Reply-To thread routing
|
|
153
|
+
* for human replies that arrive without X-AAMP headers.
|
|
154
|
+
*/
|
|
155
|
+
async sendTask(opts) {
|
|
156
|
+
return this.smtpSender.sendTask(opts);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Send a task.result email (agent → system/dispatcher)
|
|
160
|
+
*/
|
|
161
|
+
async sendResult(opts) {
|
|
162
|
+
return this.smtpSender.sendResult(opts);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Send a task.help email when the agent needs human assistance
|
|
166
|
+
*/
|
|
167
|
+
async sendHelp(opts) {
|
|
168
|
+
return this.smtpSender.sendHelp(opts);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Download a blob (attachment) by its JMAP blobId.
|
|
172
|
+
* Use this to retrieve attachment content from received TaskDispatch or TaskResult messages.
|
|
173
|
+
* Returns the raw binary content as a Buffer.
|
|
174
|
+
*/
|
|
175
|
+
async downloadBlob(blobId, filename) {
|
|
176
|
+
return this.jmapClient.downloadBlob(blobId, filename);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Reconcile recent mailbox contents via JMAP HTTP to catch messages missed by
|
|
180
|
+
* a flaky WebSocket path. Safe to call periodically; duplicate processing is
|
|
181
|
+
* suppressed by the JMAP push client.
|
|
182
|
+
*/
|
|
183
|
+
async reconcileRecentEmails(limit) {
|
|
184
|
+
return this.jmapClient.reconcileRecentEmails(limit);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Verify SMTP connectivity
|
|
188
|
+
*/
|
|
189
|
+
async verifySmtp() {
|
|
190
|
+
return this.smtpSender.verify();
|
|
191
|
+
}
|
|
192
|
+
get email() {
|
|
193
|
+
return this.config.email;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @byted-meego/aamp-sdk — Node.js SDK for AAMP Service
|
|
3
|
+
*
|
|
4
|
+
* Main exports:
|
|
5
|
+
* - AampClient: combined JMAP push receiver + SMTP sender
|
|
6
|
+
* - parseAampHeaders: parse AAMP headers from raw email headers
|
|
7
|
+
* - Types
|
|
8
|
+
*/
|
|
9
|
+
export { AampClient } from './client.js';
|
|
10
|
+
export { parseAampHeaders, normalizeHeaders, buildDispatchHeaders, buildResultHeaders, buildHelpHeaders, buildAckHeaders } from './parser.js';
|
|
11
|
+
export { JmapPushClient } from './jmap-push.js';
|
|
12
|
+
export { SmtpSender } from './smtp-sender.js';
|
|
13
|
+
export type { AampIntent, TaskStatus, AampAttachment, ReceivedAttachment, StructuredResultField, TaskDispatch, TaskResult, TaskHelp, TaskAck, HumanReply, AampMessage, AampClientConfig, AampClientEvents, SendTaskOptions, SendResultOptions, SendHelpOptions, } from './types.js';
|
|
14
|
+
export { AAMP_HEADER } from './types.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7I,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,OAAO,EACP,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @byted-meego/aamp-sdk — Node.js SDK for AAMP Service
|
|
3
|
+
*
|
|
4
|
+
* Main exports:
|
|
5
|
+
* - AampClient: combined JMAP push receiver + SMTP sender
|
|
6
|
+
* - parseAampHeaders: parse AAMP headers from raw email headers
|
|
7
|
+
* - Types
|
|
8
|
+
*/
|
|
9
|
+
export { AampClient } from './client.js';
|
|
10
|
+
export { parseAampHeaders, normalizeHeaders, buildDispatchHeaders, buildResultHeaders, buildHelpHeaders, buildAckHeaders } from './parser.js';
|
|
11
|
+
export { JmapPushClient } from './jmap-push.js';
|
|
12
|
+
export { SmtpSender } from './smtp-sender.js';
|
|
13
|
+
export { AAMP_HEADER } from './types.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7I,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAsB7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JMAP WebSocket Push Client
|
|
3
|
+
*
|
|
4
|
+
* Connects to Stalwart's JMAP WebSocket endpoint and subscribes to
|
|
5
|
+
* Email StateChange events. When a new email arrives in the agent's
|
|
6
|
+
* mailbox, fetches it via JMAP and emits the parsed AAMP headers.
|
|
7
|
+
*
|
|
8
|
+
* Protocol: RFC 8887 (JMAP over WebSocket)
|
|
9
|
+
* Ref: https://www.rfc-editor.org/rfc/rfc8887
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
export declare class JmapPushClient extends EventEmitter {
|
|
13
|
+
private ws;
|
|
14
|
+
private session;
|
|
15
|
+
private reconnectTimer;
|
|
16
|
+
private pollTimer;
|
|
17
|
+
private pingTimer;
|
|
18
|
+
private readonly seenMessageIds;
|
|
19
|
+
private connected;
|
|
20
|
+
private pollingActive;
|
|
21
|
+
private running;
|
|
22
|
+
private connecting;
|
|
23
|
+
/** JMAP Email state — tracks processed position; null = not yet initialized */
|
|
24
|
+
private emailState;
|
|
25
|
+
private readonly startedAtMs;
|
|
26
|
+
private readonly email;
|
|
27
|
+
private readonly password;
|
|
28
|
+
private readonly jmapUrl;
|
|
29
|
+
private readonly reconnectInterval;
|
|
30
|
+
private readonly rejectUnauthorized;
|
|
31
|
+
private readonly pingIntervalMs;
|
|
32
|
+
constructor(opts: {
|
|
33
|
+
email: string;
|
|
34
|
+
password: string;
|
|
35
|
+
jmapUrl: string;
|
|
36
|
+
reconnectInterval?: number;
|
|
37
|
+
/** Whether to reject unauthorized TLS certificates (default: true) */
|
|
38
|
+
rejectUnauthorized?: boolean;
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Start the JMAP Push listener
|
|
42
|
+
*/
|
|
43
|
+
start(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Stop the JMAP Push listener
|
|
46
|
+
*/
|
|
47
|
+
stop(): void;
|
|
48
|
+
private getAuthHeader;
|
|
49
|
+
/**
|
|
50
|
+
* Fetch the JMAP session object
|
|
51
|
+
*/
|
|
52
|
+
private fetchSession;
|
|
53
|
+
/**
|
|
54
|
+
* Perform a JMAP API call
|
|
55
|
+
*/
|
|
56
|
+
private jmapCall;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize emailState by fetching the current state without loading any emails.
|
|
59
|
+
* Called on first connect so we only process emails that arrive AFTER this point.
|
|
60
|
+
*/
|
|
61
|
+
private initEmailState;
|
|
62
|
+
/**
|
|
63
|
+
* Fetch only emails created since `sinceState` using Email/changes.
|
|
64
|
+
* Updates `this.emailState` to the new state after fetching.
|
|
65
|
+
* Returns [] and resets state if the server cannot calculate changes (state too old).
|
|
66
|
+
*/
|
|
67
|
+
private fetchEmailsSince;
|
|
68
|
+
/**
|
|
69
|
+
* Process a received email.
|
|
70
|
+
*
|
|
71
|
+
* Priority:
|
|
72
|
+
* 1. If X-AAMP-Intent is present → emit typed AAMP event (task.dispatch / task.result / task.help)
|
|
73
|
+
* 2. If In-Reply-To is present → emit 'reply' event so the application layer can
|
|
74
|
+
* resolve the thread (inReplyTo → taskId via Redis/DB) and handle human replies.
|
|
75
|
+
* 3. Otherwise → ignore (not an AAMP-related email)
|
|
76
|
+
*/
|
|
77
|
+
private processEmail;
|
|
78
|
+
private fetchRecentEmails;
|
|
79
|
+
private shouldProcessBootstrapEmail;
|
|
80
|
+
/**
|
|
81
|
+
* Connect to JMAP WebSocket
|
|
82
|
+
*/
|
|
83
|
+
private connect;
|
|
84
|
+
private startPingHeartbeat;
|
|
85
|
+
private stopPingHeartbeat;
|
|
86
|
+
private handleStateChange;
|
|
87
|
+
private scheduleReconnect;
|
|
88
|
+
isConnected(): boolean;
|
|
89
|
+
isUsingPollingFallback(): boolean;
|
|
90
|
+
private stopPolling;
|
|
91
|
+
private startPolling;
|
|
92
|
+
/**
|
|
93
|
+
* Download a blob (attachment) by its JMAP blobId.
|
|
94
|
+
* Returns the raw binary content as a Buffer.
|
|
95
|
+
*/
|
|
96
|
+
downloadBlob(blobId: string, filename?: string): Promise<Buffer>;
|
|
97
|
+
/**
|
|
98
|
+
* Actively reconcile recent mailbox contents via JMAP HTTP.
|
|
99
|
+
* Useful as a safety net when the WebSocket stays "connected"
|
|
100
|
+
* but a notification is missed by an intermediate layer.
|
|
101
|
+
*/
|
|
102
|
+
reconcileRecentEmails(limit?: number): Promise<number>;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=jmap-push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jmap-push.d.ts","sourceRoot":"","sources":["../src/jmap-push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAoDrC,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoB;IACnD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAQ;IAC1B,+EAA+E;IAC/E,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IAEzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAO;gBAE1B,IAAI,EAAE;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,MAAM,CAAA;QAChB,OAAO,EAAE,MAAM,CAAA;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,sEAAsE;QACtE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAC7B;IASD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAuBZ,OAAO,CAAC,aAAa;IAKrB;;OAEG;YACW,YAAY;IAa1B;;OAEG;YACW,QAAQ;IA+BtB;;;OAGG;YACW,cAAc;IAU5B;;;;OAIG;YACW,gBAAgB;IA+C9B;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;YAsFN,iBAAiB;IAuC/B,OAAO,CAAC,2BAA2B;IAOnC;;OAEG;YACW,OAAO;IAoHrB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,iBAAiB;YAOX,iBAAiB;IA0B/B,OAAO,CAAC,iBAAiB;IAWzB,WAAW,IAAI,OAAO;IAItB,sBAAsB,IAAI,OAAO;IAIjC,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,YAAY;IAsDpB;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiEtE;;;;OAIG;IACG,qBAAqB,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC;CA2DzD"}
|