@sendora/mcp 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 +83 -0
- package/dist/connection.js +104 -0
- package/dist/docs.js +52 -0
- package/dist/documents.js +47 -0
- package/dist/errors.js +97 -0
- package/dist/exclusions.js +28 -0
- package/dist/fence.js +91 -0
- package/dist/generated/docs.json +1 -0
- package/dist/handler.js +131 -0
- package/dist/instructions.js +44 -0
- package/dist/listing.js +38 -0
- package/dist/prompts.js +52 -0
- package/dist/search.js +131 -0
- package/dist/server.js +44 -0
- package/dist/stdio.js +32 -0
- package/dist/tool-table.js +51 -0
- package/dist/tool.js +7 -0
- package/dist/tools/broadcast-input.js +83 -0
- package/dist/tools/broadcasts.js +150 -0
- package/dist/tools/docs.js +74 -0
- package/dist/tools/domains.js +133 -0
- package/dist/tools/inbound-domains.js +118 -0
- package/dist/tools/inbound.js +274 -0
- package/dist/tools/keys.js +138 -0
- package/dist/tools/message-fields.js +180 -0
- package/dist/tools/messages.js +145 -0
- package/dist/tools/send-input.js +56 -0
- package/dist/tools/send.js +140 -0
- package/dist/tools/server.js +120 -0
- package/dist/tools/servers.js +91 -0
- package/dist/tools/streams.js +81 -0
- package/dist/tools/suppressions.js +102 -0
- package/dist/tools/webhooks.js +251 -0
- package/dist/version.js +2 -0
- package/package.json +49 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolError } from '../errors.js';
|
|
3
|
+
import { READING } from '../tool.js';
|
|
4
|
+
import { serverConnectionOf } from './server.js';
|
|
5
|
+
export const suppressionOutput = z.object({
|
|
6
|
+
streamId: z.string().describe('The stream whose list the address is on.'),
|
|
7
|
+
address: z.string().describe('The recipient, lower-cased.'),
|
|
8
|
+
reason: z
|
|
9
|
+
.enum(['hard_bounce', 'spam_complaint', 'manual', 'unsubscribe'])
|
|
10
|
+
.describe('Why the server will not send to it.'),
|
|
11
|
+
messageId: z.string().nullable().describe('The message whose bounce or complaint put it here.'),
|
|
12
|
+
createdAt: z.string().describe('When it was put on the list.'),
|
|
13
|
+
});
|
|
14
|
+
export const removedOutput = z.object({
|
|
15
|
+
address: z.string().describe('The address lifted.'),
|
|
16
|
+
streamId: z
|
|
17
|
+
.string()
|
|
18
|
+
.nullable()
|
|
19
|
+
.describe('The stream whose list held it; null for the default transactional stream.'),
|
|
20
|
+
});
|
|
21
|
+
const STREAM = 'The stream whose list is meant, from get_server; the default transactional stream when left out.';
|
|
22
|
+
/** `list_suppressions` (docs/design/mcp.md §4.2): a stream's suppression list, per stream. */
|
|
23
|
+
export const listSuppressionsTool = {
|
|
24
|
+
name: 'list_suppressions',
|
|
25
|
+
class: 'read',
|
|
26
|
+
reach: 'server',
|
|
27
|
+
operations: ['GET /v1/suppressions'],
|
|
28
|
+
register: (server, { connection }) => server.registerTool('list_suppressions', {
|
|
29
|
+
title: "List a stream's suppression list",
|
|
30
|
+
description: 'Lists the addresses one stream of this server will not send to, and why: a hard bounce, a spam complaint, an unsubscribe or a manual entry. A send to one of them is refused with recipient_suppressed; a hard bounce or a manual entry can be lifted with remove_suppression.',
|
|
31
|
+
inputSchema: z.object({
|
|
32
|
+
streamId: z.uuid().optional().describe(STREAM),
|
|
33
|
+
limit: z
|
|
34
|
+
.number()
|
|
35
|
+
.int()
|
|
36
|
+
.min(1)
|
|
37
|
+
.max(100)
|
|
38
|
+
.default(20)
|
|
39
|
+
.describe('Addresses per page, 1 to 100.'),
|
|
40
|
+
after: z.uuid().optional().describe('The next value of the previous page.'),
|
|
41
|
+
}),
|
|
42
|
+
outputSchema: z.object({
|
|
43
|
+
suppressions: z.array(suppressionOutput).describe('The page of the list.'),
|
|
44
|
+
next: z
|
|
45
|
+
.string()
|
|
46
|
+
.nullable()
|
|
47
|
+
.describe('Pass as after for the next page; null on the last.'),
|
|
48
|
+
}),
|
|
49
|
+
annotations: READING,
|
|
50
|
+
}, async (query) => {
|
|
51
|
+
try {
|
|
52
|
+
const page = await serverConnectionOf(connection).sendora.suppressions.list(query);
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: 'text', text: JSON.stringify(page) }],
|
|
55
|
+
structuredContent: page,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return toolError(error);
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
const REMOVE_DESCRIPTION = "Lifts an address from one stream's suppression list so this server may send to it again on that stream: a hard bounce or a manual entry. A spam complaint or an unsubscribe cannot be lifted, since the recipient asked.";
|
|
64
|
+
/** `remove_suppression` (docs/design/mcp.md §4.2): a hard bounce or a manual entry lifted. */
|
|
65
|
+
export const removeSuppressionTool = {
|
|
66
|
+
name: 'remove_suppression',
|
|
67
|
+
class: 'write',
|
|
68
|
+
reach: 'server',
|
|
69
|
+
operations: ['POST /v1/suppressions/delete'],
|
|
70
|
+
register: (server, { connection }) => {
|
|
71
|
+
const { kind, sendora } = serverConnectionOf(connection);
|
|
72
|
+
server.registerTool('remove_suppression', {
|
|
73
|
+
title: 'Lift a suppression',
|
|
74
|
+
description: kind === 'live'
|
|
75
|
+
? `${REMOVE_DESCRIPTION} On this live server the address is a real person's: confirm with the user first.`
|
|
76
|
+
: REMOVE_DESCRIPTION,
|
|
77
|
+
inputSchema: z.object({
|
|
78
|
+
address: z.email().describe('The address to send to again.'),
|
|
79
|
+
streamId: z.uuid().optional().describe(STREAM),
|
|
80
|
+
}),
|
|
81
|
+
outputSchema: removedOutput,
|
|
82
|
+
annotations: {
|
|
83
|
+
readOnlyHint: false,
|
|
84
|
+
destructiveHint: false,
|
|
85
|
+
idempotentHint: true,
|
|
86
|
+
openWorldHint: false,
|
|
87
|
+
},
|
|
88
|
+
}, async ({ address, streamId }) => {
|
|
89
|
+
try {
|
|
90
|
+
await sendora.suppressions.delete({ address, streamId });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return toolError(error);
|
|
94
|
+
}
|
|
95
|
+
const output = { address, streamId: streamId ?? null };
|
|
96
|
+
return {
|
|
97
|
+
content: [{ type: 'text', text: JSON.stringify(output) }],
|
|
98
|
+
structuredContent: output,
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolError } from '../errors.js';
|
|
3
|
+
import { READING } from '../tool.js';
|
|
4
|
+
import { serverConnectionOf } from './server.js';
|
|
5
|
+
const eventName = z.enum([
|
|
6
|
+
'delivered',
|
|
7
|
+
'bounced',
|
|
8
|
+
'deferred',
|
|
9
|
+
'spam_complaint',
|
|
10
|
+
'unsubscribed',
|
|
11
|
+
'cap_warning',
|
|
12
|
+
'cap_reached',
|
|
13
|
+
'inbound',
|
|
14
|
+
]);
|
|
15
|
+
export const webhookOutput = z.object({
|
|
16
|
+
webhookId: z.string().describe('What the deliveries and a delete refer to.'),
|
|
17
|
+
url: z.string().describe('Where the events are posted.'),
|
|
18
|
+
events: z.array(eventName).describe('The events it receives.'),
|
|
19
|
+
streamId: z
|
|
20
|
+
.string()
|
|
21
|
+
.nullable()
|
|
22
|
+
.describe('The one stream whose message events it receives; null for every stream.'),
|
|
23
|
+
enabled: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.describe('False while switched off in the dashboard; nothing is queued for it then.'),
|
|
26
|
+
inboundContent: z
|
|
27
|
+
.enum(['full', 'reference'])
|
|
28
|
+
.describe('How the inbound event carries a received message: the parsed message, or a reference.'),
|
|
29
|
+
createdAt: z.string().describe('When it was created.'),
|
|
30
|
+
secrets: z
|
|
31
|
+
.array(z.object({
|
|
32
|
+
secretId: z.string().describe('The secret, by id; its value was shown once.'),
|
|
33
|
+
createdAt: z.string().describe('When it was made.'),
|
|
34
|
+
}))
|
|
35
|
+
.describe('The live signing secrets by id and age, oldest first; their values are never answered again.'),
|
|
36
|
+
});
|
|
37
|
+
export const createdWebhookOutput = webhookOutput.extend({
|
|
38
|
+
secret: z
|
|
39
|
+
.string()
|
|
40
|
+
.describe("The signing secret, shown this once: into the project's environment, never a file under version control."),
|
|
41
|
+
});
|
|
42
|
+
export const deliveryOutput = z.object({
|
|
43
|
+
deliveryId: z.string().describe('What a replay refers to.'),
|
|
44
|
+
event: eventName.describe('The event it carries.'),
|
|
45
|
+
messageId: z
|
|
46
|
+
.string()
|
|
47
|
+
.nullable()
|
|
48
|
+
.describe('The message it is about; null for an event about usage.'),
|
|
49
|
+
inboundMessageId: z
|
|
50
|
+
.string()
|
|
51
|
+
.nullable()
|
|
52
|
+
.describe('The received message an inbound event is about; null for every other event.'),
|
|
53
|
+
status: z
|
|
54
|
+
.enum(['pending', 'delivered', 'dead'])
|
|
55
|
+
.describe('pending until its next attempt; delivered; dead after its retries, and replayable.'),
|
|
56
|
+
attempts: z.number().int().describe('How many times it was posted.'),
|
|
57
|
+
nextAttemptAt: z.string().nullable().describe('When the next attempt is due, while pending.'),
|
|
58
|
+
lastStatusCode: z
|
|
59
|
+
.number()
|
|
60
|
+
.int()
|
|
61
|
+
.nullable()
|
|
62
|
+
.describe('The HTTP status the endpoint last answered.'),
|
|
63
|
+
lastError: z.string().nullable().describe('Why the last attempt failed, when it did.'),
|
|
64
|
+
deliveredAt: z.string().nullable().describe('When the endpoint took it.'),
|
|
65
|
+
createdAt: z.string().describe('When the event was queued.'),
|
|
66
|
+
});
|
|
67
|
+
const webhookId = z.uuid().describe('The webhook, from list_webhooks.');
|
|
68
|
+
export const createWebhookInput = z.object({
|
|
69
|
+
url: z.string().describe('An https URL on a public host, without credentials or a fragment.'),
|
|
70
|
+
events: z
|
|
71
|
+
.array(eventName)
|
|
72
|
+
.min(1)
|
|
73
|
+
.optional()
|
|
74
|
+
.describe('The events to deliver; every event when left out.'),
|
|
75
|
+
streamId: z
|
|
76
|
+
.uuid()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe('Limits the message events to one stream of the server; every stream when left out.'),
|
|
79
|
+
inboundContent: z
|
|
80
|
+
.enum(['full', 'reference'])
|
|
81
|
+
.optional()
|
|
82
|
+
.describe('How the inbound event carries a received message; full when left out.'),
|
|
83
|
+
});
|
|
84
|
+
/** `list_webhooks` (docs/design/mcp.md §4.2): the server's webhooks, their secrets by id and age alone. */
|
|
85
|
+
export const listWebhooksTool = {
|
|
86
|
+
name: 'list_webhooks',
|
|
87
|
+
class: 'read',
|
|
88
|
+
reach: 'server',
|
|
89
|
+
operations: ['GET /v1/webhooks'],
|
|
90
|
+
register: (server, { connection }) => server.registerTool('list_webhooks', {
|
|
91
|
+
title: "List the server's webhooks",
|
|
92
|
+
description: "Lists this server's webhooks: where each posts, which events and stream, whether it is switched on, and its signing secrets by id and age. A secret's value is shown once, when it is made, and never answered again.",
|
|
93
|
+
inputSchema: z.object({}),
|
|
94
|
+
outputSchema: z.object({
|
|
95
|
+
webhooks: z.array(webhookOutput).describe("The server's webhooks."),
|
|
96
|
+
}),
|
|
97
|
+
annotations: READING,
|
|
98
|
+
}, async () => {
|
|
99
|
+
try {
|
|
100
|
+
const list = await serverConnectionOf(connection).sendora.webhooks.list();
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: 'text', text: JSON.stringify(list) }],
|
|
103
|
+
structuredContent: list,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return toolError(error);
|
|
108
|
+
}
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
/** `list_webhook_deliveries` (docs/design/mcp.md §4.2): one webhook's deliveries, by status. */
|
|
112
|
+
export const listWebhookDeliveriesTool = {
|
|
113
|
+
name: 'list_webhook_deliveries',
|
|
114
|
+
class: 'read',
|
|
115
|
+
reach: 'server',
|
|
116
|
+
operations: ['GET /v1/webhooks/{id}/deliveries'],
|
|
117
|
+
register: (server, { connection }) => server.registerTool('list_webhook_deliveries', {
|
|
118
|
+
title: "List a webhook's deliveries",
|
|
119
|
+
description: "Lists the events on their way to one webhook, newest first: pending with its next attempt, delivered, or dead after its retries, with the endpoint's last answer. A dead delivery can be sent again with replay_webhook_delivery.",
|
|
120
|
+
inputSchema: z.object({
|
|
121
|
+
webhookId,
|
|
122
|
+
status: z
|
|
123
|
+
.enum(['pending', 'delivered', 'dead'])
|
|
124
|
+
.optional()
|
|
125
|
+
.describe('Deliveries in this state only.'),
|
|
126
|
+
limit: z
|
|
127
|
+
.number()
|
|
128
|
+
.int()
|
|
129
|
+
.min(1)
|
|
130
|
+
.max(100)
|
|
131
|
+
.default(20)
|
|
132
|
+
.describe('Deliveries per page, 1 to 100.'),
|
|
133
|
+
after: z.uuid().optional().describe('The next value of the previous page.'),
|
|
134
|
+
}),
|
|
135
|
+
outputSchema: z.object({
|
|
136
|
+
deliveries: z.array(deliveryOutput).describe('The page of deliveries, newest first.'),
|
|
137
|
+
next: z
|
|
138
|
+
.string()
|
|
139
|
+
.nullable()
|
|
140
|
+
.describe('Pass as after for the next page; null on the last.'),
|
|
141
|
+
}),
|
|
142
|
+
annotations: READING,
|
|
143
|
+
}, async ({ webhookId: id, ...query }) => {
|
|
144
|
+
try {
|
|
145
|
+
const page = await serverConnectionOf(connection).sendora.webhooks.deliveries(id, query);
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: 'text', text: JSON.stringify(page) }],
|
|
148
|
+
structuredContent: page,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return toolError(error);
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
};
|
|
156
|
+
/** `replay_webhook_delivery` (docs/design/mcp.md §4.2): an event queued for its webhook again. */
|
|
157
|
+
export const replayWebhookDeliveryTool = {
|
|
158
|
+
name: 'replay_webhook_delivery',
|
|
159
|
+
class: 'write',
|
|
160
|
+
reach: 'server',
|
|
161
|
+
operations: ['POST /v1/webhooks/{id}/deliveries/{deliveryId}/replay'],
|
|
162
|
+
register: (server, { connection }) => server.registerTool('replay_webhook_delivery', {
|
|
163
|
+
title: 'Replay a webhook delivery',
|
|
164
|
+
description: 'Queues one delivery of a webhook again, whatever its state, with the same payload, a fresh signature and a fresh count of attempts: for an endpoint that was down or was fixed. Each replay posts the event once more.',
|
|
165
|
+
inputSchema: z.object({
|
|
166
|
+
webhookId,
|
|
167
|
+
deliveryId: z.uuid().describe('The delivery, from list_webhook_deliveries.'),
|
|
168
|
+
}),
|
|
169
|
+
outputSchema: deliveryOutput,
|
|
170
|
+
annotations: {
|
|
171
|
+
readOnlyHint: false,
|
|
172
|
+
destructiveHint: false,
|
|
173
|
+
idempotentHint: false,
|
|
174
|
+
openWorldHint: false,
|
|
175
|
+
},
|
|
176
|
+
}, async ({ webhookId: id, deliveryId }) => {
|
|
177
|
+
try {
|
|
178
|
+
const delivery = await serverConnectionOf(connection).sendora.webhooks.replay(id, deliveryId);
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: 'text', text: JSON.stringify(delivery) }],
|
|
181
|
+
structuredContent: delivery,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return toolError(error);
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
};
|
|
189
|
+
/** `create_webhook` (docs/design/mcp.md §4.2): a webhook on a test server, its secret answered once. */
|
|
190
|
+
export const createWebhookTool = {
|
|
191
|
+
name: 'create_webhook',
|
|
192
|
+
class: 'secret',
|
|
193
|
+
reach: 'server',
|
|
194
|
+
operations: ['POST /v1/webhooks'],
|
|
195
|
+
register: (server, { connection }) => server.registerTool('create_webhook', {
|
|
196
|
+
title: 'Add a webhook',
|
|
197
|
+
description: "Adds a webhook to this test server and answers its signing secret, shown this once: put it in the project's environment, such as SENDORA_WEBHOOK_SECRET in .env, never in a file under version control, and verify every delivery with the SDK's webhooks.receive or verifyWebhook. The API takes only a public https URL, so a local endpoint needs a tunnel. Events arrive as the simulator plays out each message.",
|
|
198
|
+
inputSchema: createWebhookInput,
|
|
199
|
+
outputSchema: createdWebhookOutput,
|
|
200
|
+
annotations: {
|
|
201
|
+
readOnlyHint: false,
|
|
202
|
+
destructiveHint: false,
|
|
203
|
+
idempotentHint: false,
|
|
204
|
+
openWorldHint: false,
|
|
205
|
+
},
|
|
206
|
+
}, async (request) => {
|
|
207
|
+
try {
|
|
208
|
+
const created = await serverConnectionOf(connection).sendora.webhooks.create(request);
|
|
209
|
+
return {
|
|
210
|
+
content: [{ type: 'text', text: JSON.stringify(created) }],
|
|
211
|
+
structuredContent: created,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return toolError(error);
|
|
216
|
+
}
|
|
217
|
+
}),
|
|
218
|
+
};
|
|
219
|
+
/** `delete_webhook` (docs/design/mcp.md §4.2): a test server's webhook removed, for good. */
|
|
220
|
+
export const deleteWebhookTool = {
|
|
221
|
+
name: 'delete_webhook',
|
|
222
|
+
class: 'irreversible',
|
|
223
|
+
reach: 'server',
|
|
224
|
+
operations: ['DELETE /v1/webhooks/{id}'],
|
|
225
|
+
register: (server, { connection }) => server.registerTool('delete_webhook', {
|
|
226
|
+
title: 'Delete a webhook',
|
|
227
|
+
description: 'Deletes a webhook of this test server with its secrets and deliveries; nothing more is posted to it, and it cannot be restored.',
|
|
228
|
+
inputSchema: z.object({ webhookId }),
|
|
229
|
+
outputSchema: z.object({
|
|
230
|
+
webhookId: z.string().describe('The webhook deleted.'),
|
|
231
|
+
}),
|
|
232
|
+
annotations: {
|
|
233
|
+
readOnlyHint: false,
|
|
234
|
+
destructiveHint: true,
|
|
235
|
+
idempotentHint: true,
|
|
236
|
+
openWorldHint: false,
|
|
237
|
+
},
|
|
238
|
+
}, async ({ webhookId: id }) => {
|
|
239
|
+
try {
|
|
240
|
+
await serverConnectionOf(connection).sendora.webhooks.delete(id);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return toolError(error);
|
|
244
|
+
}
|
|
245
|
+
const output = { webhookId: id };
|
|
246
|
+
return {
|
|
247
|
+
content: [{ type: 'text', text: JSON.stringify(output) }],
|
|
248
|
+
structuredContent: output,
|
|
249
|
+
};
|
|
250
|
+
}),
|
|
251
|
+
};
|
package/dist/version.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sendora/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The official MCP server for Sendora: its documentation and API for coding agents.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sendora-mcp": "./dist/stdio.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=20.19"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://sendora.se/docs/mcp",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"sendora",
|
|
20
|
+
"mcp",
|
|
21
|
+
"email",
|
|
22
|
+
"transactional"
|
|
23
|
+
],
|
|
24
|
+
"mcpName": "se.sendora/mcp",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/server": "^2.1.0",
|
|
27
|
+
"zod": "^4.6.2",
|
|
28
|
+
"@sendora/sdk": "^3.6.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@anthropic-ai/mcpb": "2.1.2",
|
|
32
|
+
"@modelcontextprotocol/client": "^2.1.0",
|
|
33
|
+
"@modelcontextprotocol/conformance": "0.2.0-alpha.11",
|
|
34
|
+
"@modelcontextprotocol/node": "^2.1.0",
|
|
35
|
+
"esbuild": "^0.28.2",
|
|
36
|
+
"fast-check": "^4.10.2",
|
|
37
|
+
"fflate": "^0.8.3",
|
|
38
|
+
"typescript": "~7.0.2",
|
|
39
|
+
"vitest": "^4.1.11",
|
|
40
|
+
"@sendora/db": "0.0.0",
|
|
41
|
+
"@sendora/api": "0.0.0",
|
|
42
|
+
"@sendora/domain": "0.0.0",
|
|
43
|
+
"@sendora/observability": "0.0.0",
|
|
44
|
+
"@sendora/site": "0.0.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "pnpm --filter @sendora/sdk build && node scripts/build-docs.ts && node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node -e \"fs.mkdirSync('dist/generated',{recursive:true});fs.copyFileSync('src/generated/docs.json','dist/generated/docs.json')\""
|
|
48
|
+
}
|
|
49
|
+
}
|