blujay-mcp 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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +160 -2
- package/dist/docs.d.ts +8 -0
- package/dist/docs.js +61 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +53 -0
- package/dist/files.d.ts +36 -0
- package/dist/files.js +405 -0
- package/dist/format.d.ts +11 -0
- package/dist/format.js +96 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +77 -0
- package/dist/server.d.ts +25 -0
- package/dist/server.js +871 -0
- package/package.json +52 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2026-11-10)
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Read tools: account and numbers, conversations, transcripts, who needs a reply, message status, iMessage checks, contacts, webhooks, docs search
|
|
8
|
+
- Write tools: send texts (to phone numbers, or email addresses over iMessage) with a photo, video, document (PDF, .docx, .xlsx, .pptx; iMessage or RCS) or contact card (from a link, or a file on your computer in one step), iMessage effects on follow-ups, audio files as voice memos, reactions (tapbacks and emoji), edit and unsend, read receipts, typing, group changes, contacts, opt-outs, webhooks
|
|
9
|
+
- Safety: `--read-only`, `--allow-send-to`, read/act labels on every tool, an idempotency key on every send
|
|
10
|
+
- Prompts: Who needs a reply?, Summarize a conversation, Send me a test message
|
|
11
|
+
- `--enable-invoke-api` adds `invoke_api` for any endpoint
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Blujay
|
|
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,161 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Blujay MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/blujay-mcp)
|
|
4
|
+
[](https://github.com/blujayhq/blujay-mcp/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Let Claude, Cursor, VS Code and other AI assistants work with your [Blujay](https://blujay.io) account: read conversations, see who needs a reply, and send iMessage, RCS and SMS from your Blujay phone numbers.
|
|
8
|
+
|
|
9
|
+
> **Every text is real.** Blujay has no test mode. Most assistants ask before each send unless you've set them to approve tools automatically, so try it with `--allow-send-to` limited to your own number first (see [Safety](#safety)).
|
|
10
|
+
|
|
11
|
+
## What you can ask
|
|
12
|
+
|
|
13
|
+
- "Who texted me today that I haven't answered?"
|
|
14
|
+
- "Summarize my conversation with Ava and draft a reply."
|
|
15
|
+
- "Text +1 555 555 0123: running 10 minutes late."
|
|
16
|
+
- "Send Ava the photo on my Desktop called beach.jpg."
|
|
17
|
+
- "Text Sam the lease PDF in my Downloads folder."
|
|
18
|
+
- "Send Ava happy birthday with balloons."
|
|
19
|
+
- "Did my last message to Sam get delivered?"
|
|
20
|
+
- "Does this number have iMessage?"
|
|
21
|
+
- "Set up a webhook to https://example.com/blujay for new messages."
|
|
22
|
+
- "How do I verify webhook signatures?"
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
You need Node.js 20 or later and a Blujay API key from the **API** tab in Blujay.
|
|
27
|
+
|
|
28
|
+
[](https://cursor.com/en-US/install-mcp?name=blujay&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImJsdWpheS1tY3AiXSwiZW52Ijp7IkJMVUpBWV9BUElfS0VZIjoiYmx1X3lvdXJfYXBpX2tleSJ9fQ==)
|
|
29
|
+
[](https://vscode.dev/redirect/mcp/install?name=blujay&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22blujay-mcp%22%5D%2C%22env%22%3A%7B%22BLUJAY_API_KEY%22%3A%22blu_your_api_key%22%7D%7D)
|
|
30
|
+
|
|
31
|
+
The buttons add the server with a placeholder key; replace `blu_your_api_key` with yours.
|
|
32
|
+
|
|
33
|
+
### Claude Desktop
|
|
34
|
+
|
|
35
|
+
Open **Settings → Developer → Edit Config** and add:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"blujay": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["-y", "blujay-mcp"],
|
|
43
|
+
"env": { "BLUJAY_API_KEY": "blu_your_api_key" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Restart Claude Desktop.
|
|
50
|
+
|
|
51
|
+
### Claude Code
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
claude mcp add blujay --env BLUJAY_API_KEY=blu_your_api_key -- npx -y blujay-mcp
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Cursor
|
|
58
|
+
|
|
59
|
+
In **Cursor Settings → Tools & MCP → New MCP Server**, add to `mcp.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"blujay": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "blujay-mcp"],
|
|
67
|
+
"env": { "BLUJAY_API_KEY": "blu_your_api_key" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### VS Code
|
|
74
|
+
|
|
75
|
+
Add to `.vscode/mcp.json` in your project:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"servers": {
|
|
80
|
+
"blujay": {
|
|
81
|
+
"command": "npx",
|
|
82
|
+
"args": ["-y", "blujay-mcp"],
|
|
83
|
+
"env": { "BLUJAY_API_KEY": "${input:blujay-api-key}" }
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"inputs": [
|
|
87
|
+
{ "id": "blujay-api-key", "type": "promptString", "description": "Blujay API key", "password": true }
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Other MCP clients
|
|
93
|
+
|
|
94
|
+
Run `npx -y blujay-mcp` with `BLUJAY_API_KEY` set. It speaks MCP over stdio.
|
|
95
|
+
|
|
96
|
+
## Safety
|
|
97
|
+
|
|
98
|
+
- **Assistants ask before sends.** Every tool is labeled read or act, so your client can approve reads and ask before anything reaches a person.
|
|
99
|
+
- **`--allow-send-to +15555550123`** limits every send and change to the numbers you list; try it on your own number first.
|
|
100
|
+
- **`--read-only`** leaves out every tool that sends, changes or deletes.
|
|
101
|
+
- **Files only by full path:** photos, videos, documents, contact cards and audio up to 100 MiB, checked by their contents.
|
|
102
|
+
|
|
103
|
+
Details, including what these checks can't stop, are in [SECURITY.md](SECURITY.md).
|
|
104
|
+
|
|
105
|
+
## Tools
|
|
106
|
+
|
|
107
|
+
| Tool | What it does | Reaches a person |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| `get_account` | Your workspace and phone numbers, and what each can do | |
|
|
110
|
+
| `list_conversations` | Recent conversations, optionally with one person or on one number | |
|
|
111
|
+
| `get_conversation` | A conversation as a transcript, found by phone number, email or chat ID | |
|
|
112
|
+
| `find_unanswered` | Conversations where the other person is waiting on you | |
|
|
113
|
+
| `get_message` | A message's delivery status and why it failed | |
|
|
114
|
+
| `check_imessage` | Whether a number uses iMessage | |
|
|
115
|
+
| `find_contacts` | Search contacts by name, phone or email, or list saved (or unsaved) contacts | |
|
|
116
|
+
| `list_webhooks` | Webhook subscriptions and recent delivery attempts | |
|
|
117
|
+
| `search_docs` | Search docs.blujay.io | |
|
|
118
|
+
| `send_message` | Text a person or group, or reply in a conversation, with an optional photo, video, document, contact card or audio file (sent as a voice memo) from your computer, a photo, video or contact card from a link, or an iMessage effect such as balloons or slam on a follow-up | Yes |
|
|
119
|
+
| `react_to_message` | Add or remove a tapback or an emoji reaction | Yes |
|
|
120
|
+
| `edit_message` | Edit a sent iMessage | Yes |
|
|
121
|
+
| `unsend_message` | Unsend an iMessage | Yes |
|
|
122
|
+
| `mark_conversation_read` | Mark a conversation as read | Yes (read receipt) |
|
|
123
|
+
| `set_typing` | Show or hide the typing bubble | Yes |
|
|
124
|
+
| `update_group` | Rename an iMessage group, or add or remove a person | Yes |
|
|
125
|
+
| `save_contact` | Create or update a contact, and tag it | Can: a tag can start a workflow that texts them |
|
|
126
|
+
| `opt_out_contact` | Stop all texts to a contact (off with `--allow-send-to`) | |
|
|
127
|
+
| `create_webhook` | Create a webhook subscription (off with `--allow-send-to`) | |
|
|
128
|
+
| `invoke_api` | Call any Blujay endpoint (only with `--enable-invoke-api`) | Depends |
|
|
129
|
+
|
|
130
|
+
## Prompts
|
|
131
|
+
|
|
132
|
+
Your MCP client shows these as ready-made actions:
|
|
133
|
+
|
|
134
|
+
- **Who needs a reply?** Finds the conversations waiting on you and drafts a reply for each, without sending.
|
|
135
|
+
- **Summarize a conversation** with one person.
|
|
136
|
+
- **Send me a test message** to your own phone and confirm it arrived.
|
|
137
|
+
|
|
138
|
+
## Options
|
|
139
|
+
|
|
140
|
+
| Option | Effect |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| `--read-only` | Only the tools that read |
|
|
143
|
+
| `--allow-send-to <numbers>` | Only reach these comma-separated numbers |
|
|
144
|
+
| `--enable-invoke-api` | Add `invoke_api` for any endpoint. Only `GET` with `--read-only` or `--allow-send-to` |
|
|
145
|
+
| `--help`, `--version` | |
|
|
146
|
+
|
|
147
|
+
## Troubleshooting
|
|
148
|
+
|
|
149
|
+
- **"set BLUJAY_API_KEY"**: add the key to the `env` block of your MCP client's config, then restart the client.
|
|
150
|
+
- **401, `API_KEY_REVOKED` or `API_KEY_EXPIRED`**: email support@blujay.io for a new key, then update `BLUJAY_API_KEY`.
|
|
151
|
+
- **The tools don't appear**: check that `npx -y blujay-mcp --version` works in a terminal, and restart your MCP client.
|
|
152
|
+
|
|
153
|
+
## More
|
|
154
|
+
|
|
155
|
+
- [Blujay API docs](https://docs.blujay.io)
|
|
156
|
+
- Libraries for your own code: [blujay on npm](https://www.npmjs.com/package/blujay) and [blujay on PyPI](https://pypi.org/project/blujay/)
|
|
157
|
+
- Questions and bugs: [support@blujay.io](mailto:support@blujay.io) or [open an issue](https://github.com/blujayhq/blujay-mcp/issues). Security issues: [SECURITY.md](SECURITY.md)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
[MIT](LICENSE)
|
package/dist/docs.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface Page {
|
|
2
|
+
title: string;
|
|
3
|
+
url: string;
|
|
4
|
+
body: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadDocs(fetchFn?: typeof fetch): Promise<Page[]>;
|
|
7
|
+
export declare function searchDocs(query: string, fetchFn?: typeof fetch, maxResults?: number): Promise<string>;
|
|
8
|
+
export {};
|
package/dist/docs.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Searches the Blujay docs (docs.blujay.io/llms-full.txt, loaded once per session).
|
|
2
|
+
const DOCS_URL = "https://docs.blujay.io/llms-full.txt";
|
|
3
|
+
let pages = null;
|
|
4
|
+
function parse(text) {
|
|
5
|
+
return text
|
|
6
|
+
.split(/\n(?=# )/)
|
|
7
|
+
.map((chunk) => {
|
|
8
|
+
const [heading = "", source = "", ...rest] = chunk.split("\n");
|
|
9
|
+
return {
|
|
10
|
+
title: heading.replace(/^# /, "").trim(),
|
|
11
|
+
url: source.replace(/^Source: /, "").trim(),
|
|
12
|
+
body: rest.join("\n").trim(),
|
|
13
|
+
};
|
|
14
|
+
})
|
|
15
|
+
.filter((page) => page.title && page.url.startsWith("http"));
|
|
16
|
+
}
|
|
17
|
+
export function loadDocs(fetchFn = fetch) {
|
|
18
|
+
pages ??= fetchFn(DOCS_URL)
|
|
19
|
+
.then((response) => {
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
throw new Error(`docs.blujay.io returned ${response.status}`);
|
|
22
|
+
return response.text();
|
|
23
|
+
})
|
|
24
|
+
.then(parse)
|
|
25
|
+
.catch((error) => {
|
|
26
|
+
pages = null; // try again next time
|
|
27
|
+
throw error;
|
|
28
|
+
});
|
|
29
|
+
return pages;
|
|
30
|
+
}
|
|
31
|
+
export async function searchDocs(query, fetchFn = fetch, maxResults = 3) {
|
|
32
|
+
const terms = query.toLowerCase().split(/\W+/).filter((term) => term.length > 2);
|
|
33
|
+
if (!terms.length)
|
|
34
|
+
return "Add a few words to search for.";
|
|
35
|
+
const scored = (await loadDocs(fetchFn))
|
|
36
|
+
.map((page) => {
|
|
37
|
+
const title = page.title.toLowerCase();
|
|
38
|
+
const body = page.body.toLowerCase();
|
|
39
|
+
let score = 0;
|
|
40
|
+
for (const term of terms) {
|
|
41
|
+
if (title.includes(term))
|
|
42
|
+
score += 10;
|
|
43
|
+
score += Math.min(body.split(term).length - 1, 10);
|
|
44
|
+
}
|
|
45
|
+
// Guides explain; reference pages list fields. Prefer guides when they tie.
|
|
46
|
+
if (!page.url.includes("/api-reference/"))
|
|
47
|
+
score += 0.5;
|
|
48
|
+
return { page, score };
|
|
49
|
+
})
|
|
50
|
+
.filter((result) => result.score > 1)
|
|
51
|
+
.sort((a, b) => b.score - a.score)
|
|
52
|
+
.slice(0, maxResults);
|
|
53
|
+
if (!scored.length)
|
|
54
|
+
return `Nothing in the docs matched "${query}". Browse https://docs.blujay.io.`;
|
|
55
|
+
return scored
|
|
56
|
+
.map(({ page }) => {
|
|
57
|
+
const excerpt = page.body.length > 2500 ? `${page.body.slice(0, 2500)}\n…` : page.body;
|
|
58
|
+
return `# ${page.title}\n${page.url}\n\n${excerpt}`;
|
|
59
|
+
})
|
|
60
|
+
.join("\n\n---\n\n");
|
|
61
|
+
}
|
package/dist/errors.d.ts
ADDED
package/dist/errors.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// API errors as plain sentences with the next step, so the assistant can recover
|
|
2
|
+
// or explain the problem instead of showing a bare error code.
|
|
3
|
+
import { BlujayError, BlujayTimeoutError } from "blujay";
|
|
4
|
+
const NEXT_STEP = {
|
|
5
|
+
UNAUTHORIZED: "Check that BLUJAY_API_KEY is set to the key shown under API in the Blujay sidebar.",
|
|
6
|
+
API_KEY_EXPIRED: "This API key has expired. Email support@blujay.io for a new one, then update BLUJAY_API_KEY.",
|
|
7
|
+
API_KEY_REVOKED: "This API key was revoked. Email support@blujay.io for a new one, then update BLUJAY_API_KEY.",
|
|
8
|
+
NOT_FOUND: "Check the ID, or look it up again with list_conversations or find_contacts.",
|
|
9
|
+
PRE_REPLY_LIMIT_REACHED: "This person hasn't replied yet, and the number of texts allowed before a reply is used up. Wait for them to reply.",
|
|
10
|
+
PRE_REPLY_CONTENT_BLOCKED: "Links and attachments can't be sent until the person replies. Send text only for now.",
|
|
11
|
+
FIRST_MESSAGE_LINK_NOT_ALLOWED: "A first message can't contain a link. Send text only, and share the link after they reply.",
|
|
12
|
+
PRE_REPLY_LENGTH_EXCEEDED: "Until the person replies, texts must be 300 characters or less. Shorten the text.",
|
|
13
|
+
CONTACT_MUST_TEXT_FIRST: "This number can only message people who texted it first. Send from a different number, or ask them to text you.",
|
|
14
|
+
CONTACT_NOT_VERIFIED: "This number can only message people who texted it first. Send from a different number, or ask them to text you.",
|
|
15
|
+
RECIPIENT_SUPPRESSED: "This person opted out, so Blujay won't text them.",
|
|
16
|
+
FROM_NUMBER_REQUIRED: "Pick which of your numbers to send from (see get_account) and pass it as `from`.",
|
|
17
|
+
QUEUE_FULL: "Too many messages are waiting to send from this number. Try again in a few minutes.",
|
|
18
|
+
IDEMPOTENCY_IN_PROGRESS: "Blujay is still working on the earlier attempt. Check get_conversation in a minute before sending again.",
|
|
19
|
+
RATE_LIMIT_EXCEEDED: "Too many requests right now. Wait a minute and try again.",
|
|
20
|
+
EDIT_LIMIT_REACHED: "This message can't be edited again (iMessage allows 5 edits within 15 minutes).",
|
|
21
|
+
EDIT_WINDOW_EXPIRED: "It's been more than 15 minutes, so it can't be edited. Send a new message instead.",
|
|
22
|
+
UNSEND_WINDOW_EXPIRED: "It's been more than 2 minutes, so it can't be unsent.",
|
|
23
|
+
UNSUPPORTED_ATTACHMENT_TYPE: "This file can't go out in this conversation. Documents (PDF, Word, Excel, PowerPoint) and voice memos need iMessage or RCS, and a voice memo needs a conversation that already has messages. Over SMS, send a photo, video or contact card. A link must point straight at a photo, video or contact card (or audio, sent as a voice memo).",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* @param wrote true for a tool that sends or changes something: after a timeout or lost connection
|
|
27
|
+
* it may have gone through, so the assistant must check before trying again.
|
|
28
|
+
*/
|
|
29
|
+
export function describeError(error, wrote = false) {
|
|
30
|
+
const mayHaveHappened = wrote
|
|
31
|
+
? " It may still have gone through: check with get_conversation or get_message before trying again."
|
|
32
|
+
: "";
|
|
33
|
+
if (error instanceof BlujayTimeoutError) {
|
|
34
|
+
return `Blujay didn't answer in time.${mayHaveHappened || " Try again in a moment."}`;
|
|
35
|
+
}
|
|
36
|
+
if (error instanceof BlujayError && error.statusCode === undefined) {
|
|
37
|
+
// No response at all: the connection failed or dropped.
|
|
38
|
+
return `Couldn't reach Blujay: ${error.message}.${mayHaveHappened}`;
|
|
39
|
+
}
|
|
40
|
+
if (error instanceof BlujayError) {
|
|
41
|
+
const body = error.body;
|
|
42
|
+
const code = error.code;
|
|
43
|
+
const message = body?.error?.message ?? error.message;
|
|
44
|
+
const nextStep = code ? NEXT_STEP[code] : undefined;
|
|
45
|
+
return [
|
|
46
|
+
`Blujay returned an error${error.statusCode ? ` (${error.statusCode}${code ? ` ${code}` : ""})` : ""}: ${message}`,
|
|
47
|
+
nextStep,
|
|
48
|
+
body?.error?.doc_url ? `Details: ${body.error.doc_url}` : undefined,
|
|
49
|
+
].filter(Boolean).join("\n");
|
|
50
|
+
}
|
|
51
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
52
|
+
return `Couldn't reach Blujay: ${text}.${mayHaveHappened}`;
|
|
53
|
+
}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type FileHandle } from "node:fs/promises";
|
|
2
|
+
/** Blujay's limit for one attachment (`POST /v1/attachments`). */
|
|
3
|
+
export declare const MAX_FILE_BYTES: number;
|
|
4
|
+
/** The file can't be sent; the message says why in plain words. */
|
|
5
|
+
export declare class FileRefusedError extends Error {
|
|
6
|
+
}
|
|
7
|
+
export type FileKind = "photo" | "video" | "document" | "contact card" | "audio";
|
|
8
|
+
export interface SendableFile {
|
|
9
|
+
bytes: Buffer;
|
|
10
|
+
kind: FileKind;
|
|
11
|
+
/** What Blujay is told the file is: image/jpeg, video/quicktime, text/vcard, ... */
|
|
12
|
+
contentType: string;
|
|
13
|
+
/** The name the recipient sees, with the extension that matches the contents. */
|
|
14
|
+
filename: string;
|
|
15
|
+
/** Hex SHA-256 of the bytes, so a repeat of the same file is recognised. */
|
|
16
|
+
sha256: string;
|
|
17
|
+
}
|
|
18
|
+
interface DetectedType {
|
|
19
|
+
contentType: string;
|
|
20
|
+
extension: string;
|
|
21
|
+
kind: FileKind;
|
|
22
|
+
}
|
|
23
|
+
/** The CRC-32 a zip records for each entry. */
|
|
24
|
+
export declare function crc32(bytes: Uint8Array): number;
|
|
25
|
+
/** What a file is from its bytes, or null when it isn't a photo, a video, a document, a contact card or audio. */
|
|
26
|
+
export declare function detectFileType(bytes: Buffer): DetectedType | null;
|
|
27
|
+
/** `~/Desktop/a.jpg` or `/Users/ava/Desktop/a.jpg`. A relative path depends on where the server started, so it's refused. */
|
|
28
|
+
export declare function resolveUserPath(path: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Reads exactly `size` bytes from an open file. If it holds fewer or more than when it was
|
|
31
|
+
* checked, it changed while being read, and it's refused rather than sent half-written.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readExactly(handle: FileHandle, size: number, resolved: string): Promise<Buffer>;
|
|
34
|
+
/** Reads a file to send, refusing anything that isn't a readable photo, video, document, contact card or audio file within the limit. */
|
|
35
|
+
export declare function readSendableFile(path: string): Promise<SendableFile>;
|
|
36
|
+
export {};
|