@lmsystems/mail-api 0.0.0-stage → 1.0.0
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/LICENSE +21 -0
- package/README.md +110 -2
- package/dist/api.js +46 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.js +643 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -0
- package/dist/main.js +10 -0
- package/dist/main.js.map +1 -0
- package/package.json +36 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mail-api.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,111 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mail-api: the mail-api.io command-line tool
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`mail-api` uses the mail API from the terminal. It lists and searches mail in all connected mailboxes, shows a message, sends, replies and saves drafts, and manages webhooks. It uses an API key and calls the same public API as any other client. It has no runtime dependencies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires Node.js 22.12 or later.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @lmsystems/mail-api # then run: mail-api
|
|
11
|
+
npx @lmsystems/mail-api inbox # or run it without installing it
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Sign in
|
|
15
|
+
|
|
16
|
+
Create an API key on the mail-api.io dashboard, then:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mail-api login # asks for the key; the key is not shown as you type
|
|
20
|
+
echo "$KEY" | mail-api login # or give it on standard input
|
|
21
|
+
mail-api login --url http://localhost:3001 # a local API
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`login` checks the key with the API, then saves the key and the API address in `~/.config/mail-api/config.json` (or `$XDG_CONFIG_HOME/mail-api/config.json`). Only your user can read the file. `MAIL_API_KEY` and `MAIL_API_URL` in the environment take priority over the saved values. `mail-api logout` removes the file. The key itself stays valid until you delete it on the dashboard.
|
|
25
|
+
|
|
26
|
+
## Read mail
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
mail-api mailboxes # connected mailboxes, their IDs and sync errors
|
|
30
|
+
mail-api inbox # newest mail of all mailboxes
|
|
31
|
+
mail-api inbox -m me@gmail.com -n 50 # one mailbox, 50 messages
|
|
32
|
+
mail-api search --from billing@stripe.com --after 2026-11-01
|
|
33
|
+
mail-api search -q "has:attachment invoice" # Gmail search syntax; other providers: text in the message
|
|
34
|
+
mail-api search -u -f ALL # unread mail in all Gmail labels
|
|
35
|
+
mail-api read 3 # message 3 of the last list
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`search` is the same command as `inbox`. Its filters are `--from`, `--subject`, `--query`, `--unread`, `--after`, `--before`, `--folder` and `--limit`. An asterisk marks unread mail. A list of one mailbox prints a `--cursor` for the next page. In a list of all mailboxes, a mailbox that fails does not stop the others.
|
|
39
|
+
|
|
40
|
+
`read` does not mark the message as read. For HTML-only mail, it prints the text of the HTML. The CLI removes control characters from mail before it prints it, so a sender cannot put terminal escape codes in a subject or body.
|
|
41
|
+
|
|
42
|
+
The CLI saves the IDs of the last list in `last-list.json` next to the config, so `read 3` and `reply 3` can find message 3. You can also give the IDs: `mail-api read <mailbox> <messageId>`.
|
|
43
|
+
|
|
44
|
+
## Send mail
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mail-api send --to a@x.com,b@x.com --subject "Hello" --text "Hi there"
|
|
48
|
+
mail-api send -m me@gmail.com --to a@x.com -s "Report" --text - -a report.pdf < body.txt
|
|
49
|
+
mail-api draft --to a@x.com -s "Check this first" --html "<p>Draft</p>"
|
|
50
|
+
mail-api reply 3 --text "Thanks!"
|
|
51
|
+
mail-api reply 3 --all --text "Thanks, everyone."
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`--mailbox` (`-m`) takes a mailbox ID or address. You can leave it out when you have only one mailbox. `--text -` and `--html -` read the body from standard input. `--attach` (`-a`) can be given more than one time.
|
|
55
|
+
|
|
56
|
+
Each `send` and `reply` sends a new random `Idempotency-Key`. If the connection fails after the request leaves, the CLI prints the key. Run the same command with `--idempotency-key <key>`: the API then returns the first result and does not send a second copy.
|
|
57
|
+
|
|
58
|
+
## Webhooks
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
mail-api webhooks create https://example.com/incoming # all mailboxes
|
|
62
|
+
mail-api webhooks create https://example.com/incoming -m me@gmail.com
|
|
63
|
+
mail-api webhooks # list
|
|
64
|
+
mail-api webhooks deliveries <id>
|
|
65
|
+
mail-api webhooks retry <deliveryId>
|
|
66
|
+
mail-api webhooks delete <id>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For each new message in INBOX, the API sends a signed `email.received` event to the URL. `create` prints the signing secret one time only. See "Subscribe to incoming-mail webhooks" in the [main README](../README.md) to check the signature.
|
|
70
|
+
|
|
71
|
+
## Scripts and agents
|
|
72
|
+
|
|
73
|
+
Add `--json` to any command to get the API's JSON on standard output. Errors go to standard error. The exit code is 0 for success, 1 when the API or the network fails, and 2 for wrong usage.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
mail-api inbox --json -n 5 | jq -r '.messages[] | "\(.mailboxId) \(.id) \(.subject)"'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm ci
|
|
83
|
+
npm run dev -- inbox # run from source
|
|
84
|
+
npm run check # typecheck and tests
|
|
85
|
+
npm run build && npm link # put this build on your PATH as "mail-api"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The tests run each command against a fake API.
|
|
89
|
+
|
|
90
|
+
## Release
|
|
91
|
+
|
|
92
|
+
`.github/workflows/publish-cli.yml` publishes the package to npm when a tag `cli-v<version>` is pushed. The tag must match `version` in `cli/package.json`. The workflow runs the checks, builds, and publishes with npm trusted publishing (OIDC), so GitHub holds no npm token. The repository is private, so npm makes no provenance attestation.
|
|
93
|
+
|
|
94
|
+
Each release:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cd cli
|
|
98
|
+
npm version patch --no-git-tag-version # or minor / major
|
|
99
|
+
git commit -am "CLI 1.0.1"
|
|
100
|
+
git tag cli-v1.0.1
|
|
101
|
+
git push && git push origin cli-v1.0.1
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
One-time setup, done by an owner of the npm organization `lmsystems`:
|
|
105
|
+
|
|
106
|
+
1. Run `npm whoami` and `npm org ls lmsystems`. Your account must be an owner. The organization also holds `@lmsystems/cli`, which is a different tool (`lmsys`).
|
|
107
|
+
2. Publish the first version by hand: `npm login`, then `npm publish` in this folder. `publishConfig.access` makes the scoped package public.
|
|
108
|
+
3. On npmjs.com, open the package **Settings**, then **Trusted Publisher**, and choose GitHub Actions: user `RVCA212`, repository `mail-api`, workflow `publish-cli.yml`. The first publish from the workflow must happen within 2 days, or the setting expires.
|
|
109
|
+
4. In the same settings, set **Publishing access** to require two-factor authentication and refuse tokens. The workflow keeps working, because it does not use a token.
|
|
110
|
+
|
|
111
|
+
The `repository.url` in `package.json` must match the GitHub repository exactly, or a trusted publish fails.
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
code;
|
|
4
|
+
fields;
|
|
5
|
+
constructor(status, code, message, fields) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.fields = fields;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function client(baseUrl, apiKey, fetchImpl) {
|
|
13
|
+
return async (method, path, { query, body, headers, } = {}) => {
|
|
14
|
+
const url = new URL(baseUrl + path);
|
|
15
|
+
for (const [key, value] of Object.entries(query ?? {}))
|
|
16
|
+
if (value !== undefined)
|
|
17
|
+
url.searchParams.set(key, String(value));
|
|
18
|
+
let response;
|
|
19
|
+
try {
|
|
20
|
+
response = await fetchImpl(url, {
|
|
21
|
+
method,
|
|
22
|
+
headers: {
|
|
23
|
+
authorization: `Bearer ${apiKey}`,
|
|
24
|
+
...(body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
25
|
+
...headers,
|
|
26
|
+
},
|
|
27
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
28
|
+
// Mail providers can be slow, for example an IMAP search in a large folder.
|
|
29
|
+
signal: AbortSignal.timeout(120_000),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const reason = error instanceof Error && error.name === 'TimeoutError'
|
|
34
|
+
? 'no answer in 2 minutes'
|
|
35
|
+
: String(error?.message ?? error);
|
|
36
|
+
throw new ApiError(0, 'network_error', `Cannot reach ${baseUrl} (${reason})`);
|
|
37
|
+
}
|
|
38
|
+
if (response.status === 204)
|
|
39
|
+
return undefined;
|
|
40
|
+
const data = (await response.json().catch(() => null));
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throw new ApiError(response.status, data?.error ?? 'request_failed', data?.message ?? `The API answered ${response.status}`, data?.fields);
|
|
43
|
+
return data;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB,MAAM;IACN,IAAI;IAEJ,MAAM;IAJjB,YACW,MAAc,EACd,IAAY,EACrB,OAAe,EACN,MAAyC;QAElD,KAAK,CAAC,OAAO,CAAC,CAAC;sBALN,MAAM;oBACN,IAAI;sBAEJ,MAAM;IAGjB,CAAC;CACF;AAQD,MAAM,UAAU,MAAM,CAAC,OAAe,EAAE,MAAc,EAAE,SAAuB;IAC7E,OAAO,KAAK,EACV,MAAc,EACd,IAAY,EACZ,EACE,KAAK,EACL,IAAI,EACJ,OAAO,GACR,GAAwE,EAAE,EAC/D,EAAE;QACd,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YACpD,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;gBAC9B,MAAM;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,MAAM,EAAE;oBACjC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;oBACrE,GAAG,OAAO;iBACX;gBACD,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7D,4EAA4E;gBAC5E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC;aACrC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,GACV,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;gBACrD,CAAC,CAAC,wBAAwB;gBAC1B,CAAC,CAAC,MAAM,CAAE,KAAe,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;YACjD,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE,eAAe,EAAE,gBAAgB,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAc,CAAC;QACnD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAI7C,CAAC;QACT,IAAI,CAAC,QAAQ,CAAC,EAAE;YACd,MAAM,IAAI,QAAQ,CAChB,QAAQ,CAAC,MAAM,EACf,IAAI,EAAE,KAAK,IAAI,gBAAgB,EAC/B,IAAI,EAAE,OAAO,IAAI,oBAAoB,QAAQ,CAAC,MAAM,EAAE,EACtD,IAAI,EAAE,MAAM,CACb,CAAC;QACJ,OAAO,IAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { createInterface } from 'node:readline/promises';
|
|
5
|
+
import { Writable } from 'node:stream';
|
|
6
|
+
import { parseArgs } from 'node:util';
|
|
7
|
+
import { ApiError, client } from './api.js';
|
|
8
|
+
import { configDir, DEFAULT_API_URL, readJson, writeJson, } from './config.js';
|
|
9
|
+
class UsageError extends Error {
|
|
10
|
+
}
|
|
11
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12
|
+
const str = (value) => (typeof value === 'string' ? value : undefined);
|
|
13
|
+
const many = (value) => (Array.isArray(value) ? value.map(String) : []);
|
|
14
|
+
const addresses = (value) => many(value)
|
|
15
|
+
.flatMap((v) => v.split(','))
|
|
16
|
+
.map((v) => v.trim())
|
|
17
|
+
.filter(Boolean);
|
|
18
|
+
// Mail text is written by the sender. Remove control and text-direction characters, so that
|
|
19
|
+
// it cannot move the cursor, change colors or hide text in the terminal.
|
|
20
|
+
const clean = (text) => text.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f--]/g, '');
|
|
21
|
+
const oneLine = (text) => clean(text.replace(/\s+/g, ' ')).trim();
|
|
22
|
+
const cut = (text, max) => text.length > max ? text.slice(0, max - 1) + '…' : text;
|
|
23
|
+
const who = (list = []) => list.map((a) => (a.name ? `${a.name} <${a.address}>` : a.address)).join(', ');
|
|
24
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
25
|
+
function when(iso) {
|
|
26
|
+
const d = new Date(iso ?? '');
|
|
27
|
+
if (Number.isNaN(d.getTime()))
|
|
28
|
+
return '';
|
|
29
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
30
|
+
}
|
|
31
|
+
// The API takes ISO date-times. A plain date means the start of that day, local time.
|
|
32
|
+
function dateTime(value) {
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
const d = new Date(/^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00` : value);
|
|
36
|
+
if (Number.isNaN(d.getTime()))
|
|
37
|
+
throw new UsageError(`"${value}" is not a date. Use 2026-11-01 or 2026-11-01T09:00:00Z.`);
|
|
38
|
+
return d.toISOString();
|
|
39
|
+
}
|
|
40
|
+
function htmlToText(html) {
|
|
41
|
+
const char = (code) => (code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '');
|
|
42
|
+
return html
|
|
43
|
+
.replace(/<(script|style|head)\b[\s\S]*?<\/\1\s*>/gi, '')
|
|
44
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
45
|
+
.replace(/<\/(p|div|li|tr|h[1-6]|blockquote|table)\s*>/gi, '\n')
|
|
46
|
+
.replace(/<[^>]*>/g, '')
|
|
47
|
+
.replace(/ /gi, ' ')
|
|
48
|
+
.replace(/</gi, '<')
|
|
49
|
+
.replace(/>/gi, '>')
|
|
50
|
+
.replace(/"/gi, '"')
|
|
51
|
+
.replace(/'|'/gi, "'")
|
|
52
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, hex) => char(parseInt(hex, 16)))
|
|
53
|
+
.replace(/&#(\d+);/g, (_, dec) => char(Number(dec)))
|
|
54
|
+
.replace(/&/gi, '&')
|
|
55
|
+
.replace(/[ \t]+\n/g, '\n')
|
|
56
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
57
|
+
.trim();
|
|
58
|
+
}
|
|
59
|
+
function table(io, headers, rows) {
|
|
60
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
61
|
+
const max = io.stdout.columns;
|
|
62
|
+
return ([headers, ...rows]
|
|
63
|
+
.map((row) => {
|
|
64
|
+
const line = row
|
|
65
|
+
.map((cell, i) => (i === row.length - 1 ? cell : cell.padEnd(widths[i])))
|
|
66
|
+
.join(' ')
|
|
67
|
+
.trimEnd();
|
|
68
|
+
return max ? cut(line, max) : line;
|
|
69
|
+
})
|
|
70
|
+
.join('\n') + '\n');
|
|
71
|
+
}
|
|
72
|
+
async function mapLimit(items, limit, fn) {
|
|
73
|
+
const results = new Array(items.length);
|
|
74
|
+
let next = 0;
|
|
75
|
+
const worker = async () => {
|
|
76
|
+
while (next < items.length) {
|
|
77
|
+
const index = next++;
|
|
78
|
+
results[index] = await fn(items[index]);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
async function readAll(stream) {
|
|
85
|
+
const chunks = [];
|
|
86
|
+
for await (const chunk of stream)
|
|
87
|
+
chunks.push(Buffer.from(chunk));
|
|
88
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
89
|
+
}
|
|
90
|
+
async function askSecret(io, question) {
|
|
91
|
+
io.stderr.write(question);
|
|
92
|
+
// Readline writes the typed characters to its output. Drop them, so the key is not shown.
|
|
93
|
+
const silent = new Writable({ write: (_chunk, _encoding, done) => done() });
|
|
94
|
+
const rl = createInterface({ input: io.stdin, output: silent, terminal: true });
|
|
95
|
+
rl.on('SIGINT', () => {
|
|
96
|
+
io.stderr.write('\n');
|
|
97
|
+
process.exit(130);
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
return await rl.question('');
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
rl.close();
|
|
104
|
+
io.stderr.write('\n');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const write = (ctx, text) => {
|
|
108
|
+
ctx.io.stdout.write(text.endsWith('\n') ? text : text + '\n');
|
|
109
|
+
};
|
|
110
|
+
const show = (ctx, data, human) => write(ctx, ctx.json ? JSON.stringify(data, null, 2) : human());
|
|
111
|
+
const noArgs = (args) => {
|
|
112
|
+
if (args.length)
|
|
113
|
+
throw new UsageError(`Unexpected argument "${args[0]}".`);
|
|
114
|
+
};
|
|
115
|
+
const messagePath = (ref) => `/v1/accounts/${ref.mailboxId}/messages/${encodeURIComponent(ref.messageId)}`;
|
|
116
|
+
// A mailbox ID, a mailbox address, or nothing when the user has only one mailbox.
|
|
117
|
+
async function findMailbox(api, value) {
|
|
118
|
+
if (value && UUID.test(value))
|
|
119
|
+
return value;
|
|
120
|
+
const { accounts } = await api('GET', '/v1/accounts');
|
|
121
|
+
if (value) {
|
|
122
|
+
const found = accounts.find((a) => a.email.toLowerCase() === value.toLowerCase());
|
|
123
|
+
if (!found)
|
|
124
|
+
throw new UsageError(`No connected mailbox has the address ${value}. Run "mail-api mailboxes".`);
|
|
125
|
+
return found._id;
|
|
126
|
+
}
|
|
127
|
+
if (accounts.length === 1)
|
|
128
|
+
return accounts[0]._id;
|
|
129
|
+
throw new UsageError(accounts.length
|
|
130
|
+
? 'You have more than one mailbox. Give --mailbox with its ID or address.'
|
|
131
|
+
: 'No mailbox is connected. Connect one on the mail-api.io dashboard.');
|
|
132
|
+
}
|
|
133
|
+
// "3" is row 3 of the last list. Two arguments are a mailbox and a message ID.
|
|
134
|
+
async function target(ctx, api, args) {
|
|
135
|
+
if (args.length === 1 && /^\d+$/.test(args[0])) {
|
|
136
|
+
const list = await readJson(ctx.files.list);
|
|
137
|
+
const ref = list?.[Number(args[0]) - 1];
|
|
138
|
+
if (!ref)
|
|
139
|
+
throw new UsageError(list
|
|
140
|
+
? `The last list has no message ${args[0]}.`
|
|
141
|
+
: 'Run "mail-api inbox" first, then use a number from its list.');
|
|
142
|
+
return ref;
|
|
143
|
+
}
|
|
144
|
+
if (args.length === 2)
|
|
145
|
+
return { mailboxId: await findMailbox(api, args[0]), messageId: args[1] };
|
|
146
|
+
throw new UsageError('Give a number from the last list, or a mailbox and a message ID.');
|
|
147
|
+
}
|
|
148
|
+
async function content(io, values) {
|
|
149
|
+
let text = str(values.text);
|
|
150
|
+
let html = str(values.html);
|
|
151
|
+
if (text === '-' && html === '-')
|
|
152
|
+
throw new UsageError('Only one of --text and --html can read standard input.');
|
|
153
|
+
if (text === '-')
|
|
154
|
+
text = await readAll(io.stdin);
|
|
155
|
+
if (html === '-')
|
|
156
|
+
html = await readAll(io.stdin);
|
|
157
|
+
if (!text && !html)
|
|
158
|
+
throw new UsageError('Give --text or --html. Use - to read the body from standard input.');
|
|
159
|
+
const attachments = await Promise.all(many(values.attach).map(async (file) => ({
|
|
160
|
+
filename: basename(file),
|
|
161
|
+
contentBase64: (await readFile(file)).toString('base64'),
|
|
162
|
+
})));
|
|
163
|
+
return {
|
|
164
|
+
...(text ? { text } : {}),
|
|
165
|
+
...(html ? { html } : {}),
|
|
166
|
+
...(attachments.length ? { attachments } : {}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
async function newMail(io, values) {
|
|
170
|
+
const to = addresses(values.to);
|
|
171
|
+
if (!to.length)
|
|
172
|
+
throw new UsageError('Give at least one --to address.');
|
|
173
|
+
const subject = str(values.subject);
|
|
174
|
+
if (subject === undefined)
|
|
175
|
+
throw new UsageError('Give --subject.');
|
|
176
|
+
const cc = addresses(values.cc);
|
|
177
|
+
const bcc = addresses(values.bcc);
|
|
178
|
+
return {
|
|
179
|
+
to,
|
|
180
|
+
...(cc.length ? { cc } : {}),
|
|
181
|
+
...(bcc.length ? { bcc } : {}),
|
|
182
|
+
subject,
|
|
183
|
+
...(await content(io, values)),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
// The API keeps the result of each idempotency key, so the same key never sends twice.
|
|
187
|
+
async function sendOnce(api, path, body, key) {
|
|
188
|
+
try {
|
|
189
|
+
return await api('POST', path, {
|
|
190
|
+
body,
|
|
191
|
+
headers: { 'idempotency-key': key },
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
// The request may have reached the API before the connection failed.
|
|
196
|
+
if (error instanceof ApiError && error.status === 0)
|
|
197
|
+
throw new ApiError(0, error.code, `${error.message}. The mail may have been sent. To try again safely, run the same command with --idempotency-key ${key}`);
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const sent = (result, to) => `Sent${to ? ` to ${to}` : ''}.` +
|
|
202
|
+
(result.rejected?.length ? `\nThe mail server refused: ${result.rejected.join(', ')}` : '');
|
|
203
|
+
const MAILBOX = { mailbox: { type: 'string', short: 'm' } };
|
|
204
|
+
const BODY = {
|
|
205
|
+
text: { type: 'string' },
|
|
206
|
+
html: { type: 'string' },
|
|
207
|
+
attach: { type: 'string', short: 'a', multiple: true },
|
|
208
|
+
};
|
|
209
|
+
const MAIL = {
|
|
210
|
+
...MAILBOX,
|
|
211
|
+
to: { type: 'string', multiple: true },
|
|
212
|
+
cc: { type: 'string', multiple: true },
|
|
213
|
+
bcc: { type: 'string', multiple: true },
|
|
214
|
+
subject: { type: 'string', short: 's' },
|
|
215
|
+
...BODY,
|
|
216
|
+
};
|
|
217
|
+
const KEY = { 'idempotency-key': { type: 'string' } };
|
|
218
|
+
const MAIL_HELP = ` -m, --mailbox ID|ADDRESS The mailbox to send from. Needed when you have more than one
|
|
219
|
+
--to ADDRESS Repeat it, or separate the addresses with commas
|
|
220
|
+
--cc, --bcc ADDRESS The same as --to
|
|
221
|
+
-s, --subject TEXT
|
|
222
|
+
--text TEXT Plain-text body. Use - to read it from standard input
|
|
223
|
+
--html HTML HTML body. Use - to read it from standard input
|
|
224
|
+
-a, --attach FILE Attach a file. Repeat it for more files`;
|
|
225
|
+
const KEY_HELP = ` --idempotency-key KEY The same key never sends twice. Default: a new random key`;
|
|
226
|
+
const COMMANDS = {
|
|
227
|
+
login: {
|
|
228
|
+
usage: `mail-api login [--url URL]
|
|
229
|
+
Asks for an API key from the mail-api.io dashboard and saves it. To give the key
|
|
230
|
+
without a prompt, send it to standard input: echo "$KEY" | mail-api login
|
|
231
|
+
--url URL The API address. Default: ${DEFAULT_API_URL}`,
|
|
232
|
+
options: { url: { type: 'string' } },
|
|
233
|
+
async run(ctx, values, args) {
|
|
234
|
+
noArgs(args);
|
|
235
|
+
const apiUrl = (str(values.url) ?? ctx.io.env.MAIL_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, '');
|
|
236
|
+
if (!URL.canParse(apiUrl))
|
|
237
|
+
throw new UsageError(`"${apiUrl}" is not a URL.`);
|
|
238
|
+
const apiKey = (ctx.io.stdin.isTTY ? await askSecret(ctx.io, 'API key: ') : await readAll(ctx.io.stdin)).trim();
|
|
239
|
+
if (!apiKey)
|
|
240
|
+
throw new UsageError('No API key given. Create one on the dashboard.');
|
|
241
|
+
try {
|
|
242
|
+
await client(apiUrl, apiKey, ctx.io.fetch)('GET', '/v1/accounts');
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// 402: the key is good, but the subscription is not active. Save it anyway.
|
|
246
|
+
if (!(error instanceof ApiError && error.status === 402))
|
|
247
|
+
throw error;
|
|
248
|
+
ctx.io.stderr.write(`Warning: ${error.message}\n`);
|
|
249
|
+
}
|
|
250
|
+
const settings = { apiKey, apiUrl };
|
|
251
|
+
await writeJson(ctx.files.settings, settings);
|
|
252
|
+
write(ctx, `Saved the API key in ${ctx.files.settings}.`);
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
logout: {
|
|
256
|
+
usage: 'mail-api logout\nRemoves the saved API key from this computer.',
|
|
257
|
+
async run(ctx, _values, args) {
|
|
258
|
+
noArgs(args);
|
|
259
|
+
await rm(ctx.files.settings, { force: true });
|
|
260
|
+
await rm(ctx.files.list, { force: true });
|
|
261
|
+
write(ctx, 'Removed the saved API key. The key still works until you delete it on the dashboard.');
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
mailboxes: {
|
|
265
|
+
usage: 'mail-api mailboxes\nLists the connected mailboxes.',
|
|
266
|
+
async run(ctx, _values, args) {
|
|
267
|
+
noArgs(args);
|
|
268
|
+
const { accounts } = await (await ctx.api())('GET', '/v1/accounts');
|
|
269
|
+
show(ctx, { mailboxes: accounts }, () => accounts.length
|
|
270
|
+
? table(ctx.io, ['ID', 'ADDRESS', 'PROVIDER', 'LAST SYNC', 'ERROR'], accounts.map((a) => [
|
|
271
|
+
a._id,
|
|
272
|
+
a.email,
|
|
273
|
+
a.provider,
|
|
274
|
+
when(a.sync?.lastSyncedAt),
|
|
275
|
+
oneLine(a.sync?.lastError ?? ''),
|
|
276
|
+
]))
|
|
277
|
+
: 'No mailbox is connected. Connect one on the mail-api.io dashboard.');
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
inbox: {
|
|
281
|
+
usage: `mail-api inbox [options] (or: mail-api search [options])
|
|
282
|
+
Lists mail, newest first. Without --mailbox, it lists mail from all mailboxes.
|
|
283
|
+
-m, --mailbox ID|ADDRESS Only this mailbox
|
|
284
|
+
-f, --folder NAME Default INBOX. For Gmail, ALL searches all mail
|
|
285
|
+
-q, --query TEXT Gmail: Gmail search syntax. Others: text in the message
|
|
286
|
+
--from ADDRESS Only mail from this address
|
|
287
|
+
-s, --subject TEXT Text in the subject
|
|
288
|
+
-u, --unread Only unread mail
|
|
289
|
+
--after DATE Received after this date (2026-11-01 or ISO 8601)
|
|
290
|
+
--before DATE Received before this date
|
|
291
|
+
-n, --limit N 1 to 100. Default 20
|
|
292
|
+
--cursor CURSOR The next page, from an earlier list of one mailbox`,
|
|
293
|
+
options: {
|
|
294
|
+
...MAILBOX,
|
|
295
|
+
folder: { type: 'string', short: 'f' },
|
|
296
|
+
query: { type: 'string', short: 'q' },
|
|
297
|
+
from: { type: 'string' },
|
|
298
|
+
subject: { type: 'string', short: 's' },
|
|
299
|
+
unread: { type: 'boolean', short: 'u' },
|
|
300
|
+
after: { type: 'string' },
|
|
301
|
+
before: { type: 'string' },
|
|
302
|
+
limit: { type: 'string', short: 'n' },
|
|
303
|
+
cursor: { type: 'string' },
|
|
304
|
+
},
|
|
305
|
+
async run(ctx, values, args) {
|
|
306
|
+
if (args.length)
|
|
307
|
+
throw new UsageError(`Unexpected argument "${args[0]}". Give search text with --query.`);
|
|
308
|
+
const limit = Number(str(values.limit) ?? 20);
|
|
309
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
310
|
+
throw new UsageError('--limit must be a number from 1 to 100.');
|
|
311
|
+
const query = {
|
|
312
|
+
folder: str(values.folder),
|
|
313
|
+
q: str(values.query),
|
|
314
|
+
from: str(values.from),
|
|
315
|
+
subject: str(values.subject),
|
|
316
|
+
unread: values.unread === true ? true : undefined,
|
|
317
|
+
after: dateTime(str(values.after)),
|
|
318
|
+
before: dateTime(str(values.before)),
|
|
319
|
+
limit,
|
|
320
|
+
};
|
|
321
|
+
const cursor = str(values.cursor);
|
|
322
|
+
const api = await ctx.api();
|
|
323
|
+
let boxes;
|
|
324
|
+
if (str(values.mailbox))
|
|
325
|
+
boxes = [{ _id: await findMailbox(api, str(values.mailbox)) }];
|
|
326
|
+
else if (cursor)
|
|
327
|
+
throw new UsageError('--cursor needs --mailbox.');
|
|
328
|
+
else
|
|
329
|
+
boxes = (await api('GET', '/v1/accounts')).accounts;
|
|
330
|
+
const results = await mapLimit(boxes, 4, async (box) => {
|
|
331
|
+
try {
|
|
332
|
+
const page = await api('GET', `/v1/accounts/${box._id}/messages`, {
|
|
333
|
+
query: { ...query, cursor },
|
|
334
|
+
});
|
|
335
|
+
return { box, page };
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
if (boxes.length === 1)
|
|
339
|
+
throw error;
|
|
340
|
+
return { box, error: error instanceof Error ? error.message : String(error) };
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
const messages = results
|
|
344
|
+
.flatMap((r) => (r.page?.messages ?? []).map((m) => ({
|
|
345
|
+
mailboxId: r.box._id,
|
|
346
|
+
...(r.box.email ? { mailbox: r.box.email } : {}),
|
|
347
|
+
...m,
|
|
348
|
+
})))
|
|
349
|
+
.sort((a, b) => Date.parse(b.receivedAt) - Date.parse(a.receivedAt))
|
|
350
|
+
.slice(0, limit);
|
|
351
|
+
const refs = messages.map((m) => ({ mailboxId: m.mailboxId, messageId: m.id }));
|
|
352
|
+
await writeJson(ctx.files.list, refs);
|
|
353
|
+
const errors = results
|
|
354
|
+
.filter((r) => r.error)
|
|
355
|
+
.map((r) => ({ mailboxId: r.box._id, mailbox: r.box.email, error: r.error }));
|
|
356
|
+
// A cursor is good only for its own mailbox, so it is shown only for a list of one mailbox.
|
|
357
|
+
const nextCursor = boxes.length === 1 ? results[0]?.page?.nextCursor : undefined;
|
|
358
|
+
const more = boxes.length > 1 ? results.filter((r) => r.page?.nextCursor) : [];
|
|
359
|
+
if (ctx.json)
|
|
360
|
+
return write(ctx, JSON.stringify({
|
|
361
|
+
messages,
|
|
362
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
363
|
+
...(more.length ? { moreInMailboxes: more.map((r) => r.box._id) } : {}),
|
|
364
|
+
...(errors.length ? { errors } : {}),
|
|
365
|
+
}, null, 2));
|
|
366
|
+
for (const e of errors)
|
|
367
|
+
ctx.io.stderr.write(`Could not list ${e.mailbox}: ${e.error}\n`);
|
|
368
|
+
if (!messages.length)
|
|
369
|
+
return write(ctx, 'No messages.');
|
|
370
|
+
const all = boxes.length > 1;
|
|
371
|
+
let text = table(ctx.io, ['#', 'DATE', ...(all ? ['MAILBOX'] : []), 'FROM', 'SUBJECT'], messages.map((m, i) => [
|
|
372
|
+
`${i + 1}${m.read ? '' : '*'}`,
|
|
373
|
+
when(m.receivedAt),
|
|
374
|
+
...(all ? [cut(m.mailbox ?? '', 28)] : []),
|
|
375
|
+
cut(oneLine(m.from[0]?.name || m.from[0]?.address || ''), 28),
|
|
376
|
+
oneLine(m.subject) || '(no subject)',
|
|
377
|
+
]));
|
|
378
|
+
text += '* = unread. Show a message with: mail-api read <#>\n';
|
|
379
|
+
if (nextCursor)
|
|
380
|
+
text += `More mail: run the same command with --mailbox ${boxes[0]._id} --cursor ${nextCursor}\n`;
|
|
381
|
+
if (more.length)
|
|
382
|
+
text += `More mail in ${more.map((r) => r.box.email).join(', ')}. Use --mailbox to see more of one mailbox.\n`;
|
|
383
|
+
write(ctx, text);
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
read: {
|
|
387
|
+
usage: `mail-api read <#>
|
|
388
|
+
mail-api read <mailbox> <messageId>
|
|
389
|
+
Shows a message: <#> is its number in the last list. It does not mark the message as read.`,
|
|
390
|
+
async run(ctx, _values, args) {
|
|
391
|
+
const api = await ctx.api();
|
|
392
|
+
const m = await api('GET', messagePath(await target(ctx, api, args)));
|
|
393
|
+
show(ctx, m, () => {
|
|
394
|
+
const body = m.text?.trim() ? m.text : m.html ? htmlToText(m.html) : '';
|
|
395
|
+
const files = m.attachments ?? [];
|
|
396
|
+
return [
|
|
397
|
+
`From: ${oneLine(who(m.from))}`,
|
|
398
|
+
`To: ${oneLine(who(m.to))}`,
|
|
399
|
+
...(m.cc?.length ? [`Cc: ${oneLine(who(m.cc))}`] : []),
|
|
400
|
+
`Date: ${when(m.receivedAt)}`,
|
|
401
|
+
`Subject: ${oneLine(m.subject)}`,
|
|
402
|
+
...(files.length
|
|
403
|
+
? [
|
|
404
|
+
`Files: ${files.map((f) => `${oneLine(f.filename)} (${Math.ceil(f.size / 1024)} KB)`).join(', ')}`,
|
|
405
|
+
]
|
|
406
|
+
: []),
|
|
407
|
+
'',
|
|
408
|
+
clean(body).trim(),
|
|
409
|
+
].join('\n');
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
send: {
|
|
414
|
+
usage: `mail-api send --to ADDRESS --subject TEXT --text TEXT [options]
|
|
415
|
+
Sends a new message.
|
|
416
|
+
${MAIL_HELP}
|
|
417
|
+
${KEY_HELP}`,
|
|
418
|
+
options: { ...MAIL, ...KEY },
|
|
419
|
+
async run(ctx, values, args) {
|
|
420
|
+
noArgs(args);
|
|
421
|
+
const mail = await newMail(ctx.io, values);
|
|
422
|
+
const api = await ctx.api();
|
|
423
|
+
const mailboxId = await findMailbox(api, str(values.mailbox));
|
|
424
|
+
const key = str(values['idempotency-key']) ?? randomUUID();
|
|
425
|
+
const result = await sendOnce(api, `/v1/accounts/${mailboxId}/messages/send`, mail, key);
|
|
426
|
+
show(ctx, result, () => sent(result, mail.to.join(', ')));
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
draft: {
|
|
430
|
+
usage: `mail-api draft --to ADDRESS --subject TEXT --text TEXT [options]
|
|
431
|
+
Saves a message in the Drafts folder of the mailbox. It does not send it.
|
|
432
|
+
${MAIL_HELP}`,
|
|
433
|
+
options: MAIL,
|
|
434
|
+
async run(ctx, values, args) {
|
|
435
|
+
noArgs(args);
|
|
436
|
+
const mail = await newMail(ctx.io, values);
|
|
437
|
+
const api = await ctx.api();
|
|
438
|
+
const mailboxId = await findMailbox(api, str(values.mailbox));
|
|
439
|
+
const result = await api('POST', `/v1/accounts/${mailboxId}/drafts`, { body: mail });
|
|
440
|
+
show(ctx, result, () => 'Saved the draft. Open the Drafts folder to check it and send it.');
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
reply: {
|
|
444
|
+
usage: `mail-api reply <#> --text TEXT [options]
|
|
445
|
+
mail-api reply <mailbox> <messageId> --text TEXT [options]
|
|
446
|
+
Replies to a message in the same thread. <#> is its number in the last list.
|
|
447
|
+
--all Reply to all recipients, not only the sender
|
|
448
|
+
--text TEXT Plain-text body. Use - to read it from standard input
|
|
449
|
+
--html HTML HTML body. Use - to read it from standard input
|
|
450
|
+
-a, --attach FILE Attach a file. Repeat it for more files
|
|
451
|
+
${KEY_HELP}`,
|
|
452
|
+
options: { ...BODY, ...KEY, all: { type: 'boolean' } },
|
|
453
|
+
async run(ctx, values, args) {
|
|
454
|
+
const body = { ...(await content(ctx.io, values)), replyAll: values.all === true };
|
|
455
|
+
const api = await ctx.api();
|
|
456
|
+
const ref = await target(ctx, api, args);
|
|
457
|
+
const key = str(values['idempotency-key']) ?? randomUUID();
|
|
458
|
+
const result = await sendOnce(api, `${messagePath(ref)}/reply`, body, key);
|
|
459
|
+
show(ctx, result, () => sent(result, result.accepted?.join(', ')));
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
'webhooks list': {
|
|
463
|
+
usage: 'mail-api webhooks [list]\nLists your webhooks.',
|
|
464
|
+
async run(ctx, _values, args) {
|
|
465
|
+
noArgs(args);
|
|
466
|
+
const { webhooks } = await (await ctx.api())('GET', '/v1/webhooks');
|
|
467
|
+
show(ctx, { webhooks }, () => webhooks.length
|
|
468
|
+
? table(ctx.io, ['ID', 'URL', 'MAILBOX', 'CREATED'], webhooks.map((w) => [w._id, w.url, w.accountId ?? 'all', when(w.createdAt)]))
|
|
469
|
+
: 'No webhooks. Add one with: mail-api webhooks create <url>');
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
'webhooks create': {
|
|
473
|
+
usage: `mail-api webhooks create <url> [--mailbox ID|ADDRESS]
|
|
474
|
+
Adds a webhook. For each new message in INBOX, the API sends a signed email.received
|
|
475
|
+
event to the URL (HTTPS POST).
|
|
476
|
+
-m, --mailbox ID|ADDRESS Only mail of this mailbox. Default: all mailboxes`,
|
|
477
|
+
options: MAILBOX,
|
|
478
|
+
async run(ctx, values, args) {
|
|
479
|
+
if (args.length !== 1)
|
|
480
|
+
throw new UsageError('Give one URL.');
|
|
481
|
+
const api = await ctx.api();
|
|
482
|
+
const mailbox = str(values.mailbox);
|
|
483
|
+
const accountId = mailbox ? await findMailbox(api, mailbox) : undefined;
|
|
484
|
+
const hook = await api('POST', '/v1/webhooks', {
|
|
485
|
+
body: { url: args[0], ...(accountId ? { accountId } : {}) },
|
|
486
|
+
});
|
|
487
|
+
show(ctx, hook, () => [
|
|
488
|
+
`Added webhook ${hook.id}.`,
|
|
489
|
+
`New mail in ${mailbox ?? 'all mailboxes'} goes to ${hook.url}.`,
|
|
490
|
+
'',
|
|
491
|
+
`Signing secret: ${hook.secret}`,
|
|
492
|
+
'',
|
|
493
|
+
'Save the secret now. The API shows it only one time.',
|
|
494
|
+
'Use it to check the x-mail-signature header of each delivery.',
|
|
495
|
+
].join('\n'));
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
'webhooks delete': {
|
|
499
|
+
usage: 'mail-api webhooks delete <id>\nDeletes a webhook.',
|
|
500
|
+
async run(ctx, _values, args) {
|
|
501
|
+
if (args.length !== 1)
|
|
502
|
+
throw new UsageError('Give one webhook ID.');
|
|
503
|
+
await (await ctx.api())('DELETE', `/v1/webhooks/${encodeURIComponent(args[0])}`);
|
|
504
|
+
show(ctx, { deleted: true, id: args[0] }, () => `Deleted webhook ${args[0]}.`);
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
'webhooks deliveries': {
|
|
508
|
+
usage: `mail-api webhooks deliveries <id> [--limit N]
|
|
509
|
+
Shows the recent deliveries of a webhook, newest first.
|
|
510
|
+
-n, --limit N 1 to 100. Default 20`,
|
|
511
|
+
options: { limit: { type: 'string', short: 'n' } },
|
|
512
|
+
async run(ctx, values, args) {
|
|
513
|
+
if (args.length !== 1)
|
|
514
|
+
throw new UsageError('Give one webhook ID.');
|
|
515
|
+
const limit = Number(str(values.limit) ?? 20);
|
|
516
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
517
|
+
throw new UsageError('--limit must be a number from 1 to 100.');
|
|
518
|
+
const { deliveries } = await (await ctx.api())('GET', `/v1/webhooks/${encodeURIComponent(args[0])}/deliveries`, { query: { limit } });
|
|
519
|
+
show(ctx, { deliveries }, () => deliveries.length
|
|
520
|
+
? table(ctx.io, ['ID', 'STATUS', 'ATTEMPTS', 'HTTP', 'TIME', 'ERROR'], deliveries.map((d) => [
|
|
521
|
+
d._id,
|
|
522
|
+
d.status,
|
|
523
|
+
String(d.attempts),
|
|
524
|
+
d.lastStatus ? String(d.lastStatus) : '',
|
|
525
|
+
when(d.deliveredAt ?? d.nextAttemptAt),
|
|
526
|
+
oneLine(d.lastError ?? ''),
|
|
527
|
+
])) + 'Send a failed delivery again with: mail-api webhooks retry <ID>'
|
|
528
|
+
: 'No deliveries yet.');
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
'webhooks retry': {
|
|
532
|
+
usage: 'mail-api webhooks retry <deliveryId>\nSends a failed delivery again.',
|
|
533
|
+
async run(ctx, _values, args) {
|
|
534
|
+
if (args.length !== 1)
|
|
535
|
+
throw new UsageError('Give one delivery ID.');
|
|
536
|
+
const result = await (await ctx.api())('POST', `/v1/deliveries/${encodeURIComponent(args[0])}/retry`);
|
|
537
|
+
show(ctx, result, () => 'The delivery will be sent again soon.');
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
export const HELP = `mail-api: use mail-api.io from the terminal.
|
|
542
|
+
|
|
543
|
+
Usage: mail-api <command> [options]
|
|
544
|
+
|
|
545
|
+
Setup
|
|
546
|
+
login Save an API key from the mail-api.io dashboard
|
|
547
|
+
logout Remove the saved API key from this computer
|
|
548
|
+
mailboxes List the connected mailboxes
|
|
549
|
+
|
|
550
|
+
Mail
|
|
551
|
+
inbox List mail, newest first. Filters: --from, --query, --unread ...
|
|
552
|
+
search The same as inbox
|
|
553
|
+
read <#> Show message <#> from the last list
|
|
554
|
+
send Send a new message
|
|
555
|
+
draft Save a message in Drafts, without sending it
|
|
556
|
+
reply <#> Reply to message <#> from the last list
|
|
557
|
+
|
|
558
|
+
Webhooks: a signed HTTPS POST to your URL for each new message in INBOX
|
|
559
|
+
webhooks List webhooks
|
|
560
|
+
webhooks create <url> Add a webhook
|
|
561
|
+
webhooks delete <id> Delete a webhook
|
|
562
|
+
webhooks deliveries <id> Show the recent deliveries of a webhook
|
|
563
|
+
webhooks retry <id> Send a failed delivery again
|
|
564
|
+
|
|
565
|
+
Options for all commands
|
|
566
|
+
--json Print JSON instead of text
|
|
567
|
+
-h, --help Show the options of a command
|
|
568
|
+
|
|
569
|
+
MAIL_API_KEY and MAIL_API_URL in the environment take priority over the saved settings.
|
|
570
|
+
`;
|
|
571
|
+
function apiError(error) {
|
|
572
|
+
if (error.status === 401)
|
|
573
|
+
return 'The API refused the API key. Run "mail-api login" with a key from the dashboard.\n';
|
|
574
|
+
const fields = (error.fields ?? []).map((f) => ` ${f.path || 'body'}: ${f.rule}\n`).join('');
|
|
575
|
+
return `Error: ${error.message} (${error.code})\n${fields}`;
|
|
576
|
+
}
|
|
577
|
+
// Runs one command. Returns the exit code: 0 done, 1 failed, 2 wrong usage.
|
|
578
|
+
export async function run(argv, io) {
|
|
579
|
+
const rest = [...argv];
|
|
580
|
+
let name = rest.shift();
|
|
581
|
+
if (name === 'search')
|
|
582
|
+
name = 'inbox';
|
|
583
|
+
if (name === 'webhooks')
|
|
584
|
+
name = `webhooks ${rest[0] && !rest[0].startsWith('-') ? rest.shift() : 'list'}`;
|
|
585
|
+
if (!name || name === 'help' || name === '--help' || name === '-h') {
|
|
586
|
+
io.stdout.write(HELP);
|
|
587
|
+
return 0;
|
|
588
|
+
}
|
|
589
|
+
const command = COMMANDS[name];
|
|
590
|
+
if (!command) {
|
|
591
|
+
io.stderr.write(`Unknown command "${name}". Run "mail-api help".\n`);
|
|
592
|
+
return 2;
|
|
593
|
+
}
|
|
594
|
+
const dir = configDir(io.env);
|
|
595
|
+
const files = { settings: join(dir, 'config.json'), list: join(dir, 'last-list.json') };
|
|
596
|
+
const ctx = {
|
|
597
|
+
io,
|
|
598
|
+
json: false,
|
|
599
|
+
files,
|
|
600
|
+
async api() {
|
|
601
|
+
const saved = await readJson(files.settings);
|
|
602
|
+
const apiKey = io.env.MAIL_API_KEY || saved?.apiKey;
|
|
603
|
+
if (!apiKey)
|
|
604
|
+
throw new Error('No API key. Run "mail-api login", or set MAIL_API_KEY.');
|
|
605
|
+
const apiUrl = io.env.MAIL_API_URL || saved?.apiUrl || DEFAULT_API_URL;
|
|
606
|
+
return client(apiUrl.replace(/\/+$/, ''), apiKey, io.fetch);
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
try {
|
|
610
|
+
const { values, positionals } = parseArgs({
|
|
611
|
+
args: rest,
|
|
612
|
+
options: {
|
|
613
|
+
...command.options,
|
|
614
|
+
json: { type: 'boolean' },
|
|
615
|
+
help: { type: 'boolean', short: 'h' },
|
|
616
|
+
},
|
|
617
|
+
allowPositionals: true,
|
|
618
|
+
strict: true,
|
|
619
|
+
});
|
|
620
|
+
if (values.help) {
|
|
621
|
+
io.stdout.write(`Usage: ${command.usage}\n`);
|
|
622
|
+
return 0;
|
|
623
|
+
}
|
|
624
|
+
ctx.json = values.json === true;
|
|
625
|
+
await command.run(ctx, values, positionals);
|
|
626
|
+
return 0;
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
const code = error.code;
|
|
630
|
+
if (error instanceof UsageError ||
|
|
631
|
+
(typeof code === 'string' && code.startsWith('ERR_PARSE_ARGS'))) {
|
|
632
|
+
io.stderr.write(`${error.message}\nRun "mail-api ${name} --help" for its options.\n`);
|
|
633
|
+
return 2;
|
|
634
|
+
}
|
|
635
|
+
if (error instanceof ApiError) {
|
|
636
|
+
io.stderr.write(apiError(error));
|
|
637
|
+
return 1;
|
|
638
|
+
}
|
|
639
|
+
io.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
640
|
+
return 1;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
export const DEFAULT_API_URL = 'https://api.mail-api.io';
|
|
5
|
+
export function configDir(env) {
|
|
6
|
+
return join(env.XDG_CONFIG_HOME || join(env.HOME || homedir(), '.config'), 'mail-api');
|
|
7
|
+
}
|
|
8
|
+
export async function readJson(file) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(await readFile(file, 'utf8'));
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === 'ENOENT')
|
|
14
|
+
return undefined;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Only the user can read these files: config.json holds the API key.
|
|
19
|
+
export async function writeJson(file, value) {
|
|
20
|
+
await mkdir(dirname(file), { recursive: true, mode: 0o700 });
|
|
21
|
+
await writeFile(file, JSON.stringify(value, null, 2) + '\n', { mode: 0o600 });
|
|
22
|
+
// The mode option applies only when the file is new.
|
|
23
|
+
await chmod(file, 0o600);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAYzD,MAAM,UAAU,SAAS,CAAC,GAAsB;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACzE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,KAAc;IAC1D,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9E,qDAAqD;IACrD,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC"}
|
package/dist/main.js
ADDED
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IAClD,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,KAAK;CACN,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmsystems/mail-api",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Command-line tool for mail-api.io: list and search mail, send and draft email, and manage webhooks.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://mail-api.io",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/RVCA212/mail-api.git",
|
|
10
|
+
"directory": "cli"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.12"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"mail-api": "dist/main.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "node --import tsx src/main.ts",
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
30
|
+
"check": "npm run typecheck && npm test",
|
|
31
|
+
"prepack": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^26.6.5",
|
|
35
|
+
"tsx": "^4.23.15",
|
|
36
|
+
"typescript": "^7.0.2"
|
|
37
|
+
}
|
|
38
|
+
}
|