skedyul 0.1.22 → 0.1.24
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/dist/core/client.d.ts +23 -2
- package/dist/core/client.js +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.js +164 -5
- package/package.json +1 -1
package/dist/core/client.d.ts
CHANGED
|
@@ -33,13 +33,34 @@ export declare const workplace: {
|
|
|
33
33
|
list(args?: ListArgs): Promise<Workplace[]>;
|
|
34
34
|
get(id: string): Promise<Workplace>;
|
|
35
35
|
};
|
|
36
|
+
export interface CreateMessageAttachment {
|
|
37
|
+
fileId: string;
|
|
38
|
+
name: string;
|
|
39
|
+
mimeType: string;
|
|
40
|
+
size: number;
|
|
41
|
+
}
|
|
42
|
+
export interface CreateMessageType {
|
|
43
|
+
id?: string | null;
|
|
44
|
+
remoteId?: string | null;
|
|
45
|
+
message: string;
|
|
46
|
+
title?: string | null;
|
|
47
|
+
contentRaw?: string | null;
|
|
48
|
+
newChat?: boolean;
|
|
49
|
+
attachments?: CreateMessageAttachment[];
|
|
50
|
+
}
|
|
51
|
+
export interface ReceiveMessageContact {
|
|
52
|
+
id?: string;
|
|
53
|
+
identifierValue?: string;
|
|
54
|
+
}
|
|
36
55
|
export interface ReceiveMessageInput {
|
|
37
56
|
/** Communication channel ID */
|
|
38
57
|
communicationChannelId: string;
|
|
39
58
|
/** Sender's identifier (e.g., phone number, email) */
|
|
40
59
|
from: string;
|
|
41
|
-
/** Message
|
|
42
|
-
message:
|
|
60
|
+
/** Message payload */
|
|
61
|
+
message: CreateMessageType;
|
|
62
|
+
/** Optional contact metadata to associate the message */
|
|
63
|
+
contact?: ReceiveMessageContact;
|
|
43
64
|
/** Optional remote/external message ID (e.g., Twilio MessageSid) */
|
|
44
65
|
remoteId?: string;
|
|
45
66
|
}
|
package/dist/core/client.js
CHANGED
|
@@ -119,6 +119,7 @@ exports.communicationChannel = {
|
|
|
119
119
|
communicationChannelId: input.communicationChannelId,
|
|
120
120
|
from: input.from,
|
|
121
121
|
message: input.message,
|
|
122
|
+
contact: input.contact,
|
|
122
123
|
...(input.remoteId ? { remoteId: input.remoteId } : {}),
|
|
123
124
|
}));
|
|
124
125
|
return payload;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SkedyulServerConfig, SkedyulServerInstance, ToolRegistry } from './types';
|
|
2
|
-
export declare function createSkedyulServer(config: SkedyulServerConfig, registry: ToolRegistry): SkedyulServerInstance;
|
|
1
|
+
import type { SkedyulServerConfig, SkedyulServerInstance, ToolRegistry, WebhookRegistry } from './types';
|
|
2
|
+
export declare function createSkedyulServer(config: SkedyulServerConfig, registry: ToolRegistry, webhookRegistry?: WebhookRegistry): SkedyulServerInstance;
|
|
3
3
|
export declare const server: {
|
|
4
4
|
create: typeof createSkedyulServer;
|
|
5
5
|
};
|
package/dist/server.js
CHANGED
|
@@ -401,7 +401,7 @@ function getListeningPort(config) {
|
|
|
401
401
|
}
|
|
402
402
|
return config.defaultPort ?? 3000;
|
|
403
403
|
}
|
|
404
|
-
function createSkedyulServer(config, registry) {
|
|
404
|
+
function createSkedyulServer(config, registry, webhookRegistry) {
|
|
405
405
|
mergeRuntimeEnv();
|
|
406
406
|
if (config.coreApi?.service) {
|
|
407
407
|
service_1.coreApiService.register(config.coreApi.service);
|
|
@@ -476,11 +476,11 @@ function createSkedyulServer(config, registry) {
|
|
|
476
476
|
});
|
|
477
477
|
}
|
|
478
478
|
if (config.computeLayer === 'dedicated') {
|
|
479
|
-
return createDedicatedServerInstance(config, tools, callTool, state, mcpServer);
|
|
479
|
+
return createDedicatedServerInstance(config, tools, callTool, state, mcpServer, webhookRegistry);
|
|
480
480
|
}
|
|
481
|
-
return createServerlessInstance(config, tools, callTool, state, mcpServer, registry);
|
|
481
|
+
return createServerlessInstance(config, tools, callTool, state, mcpServer, registry, webhookRegistry);
|
|
482
482
|
}
|
|
483
|
-
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
|
|
483
|
+
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer, webhookRegistry) {
|
|
484
484
|
const port = getListeningPort(config);
|
|
485
485
|
const httpServer = http_1.default.createServer(async (req, res) => {
|
|
486
486
|
function sendCoreResult(result) {
|
|
@@ -493,6 +493,90 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
493
493
|
sendJSON(res, 200, state.getHealthStatus());
|
|
494
494
|
return;
|
|
495
495
|
}
|
|
496
|
+
// Handle webhook requests: /webhooks/{handle}
|
|
497
|
+
if (pathname.startsWith('/webhooks/') && webhookRegistry) {
|
|
498
|
+
const handle = pathname.slice('/webhooks/'.length);
|
|
499
|
+
const webhookDef = webhookRegistry[handle];
|
|
500
|
+
if (!webhookDef) {
|
|
501
|
+
sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
// Check if HTTP method is allowed
|
|
505
|
+
const allowedMethods = webhookDef.methods ?? ['POST'];
|
|
506
|
+
if (!allowedMethods.includes(req.method)) {
|
|
507
|
+
sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
// Read raw request body
|
|
511
|
+
let rawBody;
|
|
512
|
+
try {
|
|
513
|
+
rawBody = await readRawRequestBody(req);
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
sendJSON(res, 400, { error: 'Failed to read request body' });
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// Parse body based on content type
|
|
520
|
+
let parsedBody;
|
|
521
|
+
const contentType = req.headers['content-type'] ?? '';
|
|
522
|
+
if (contentType.includes('application/json')) {
|
|
523
|
+
try {
|
|
524
|
+
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
parsedBody = rawBody;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
parsedBody = rawBody;
|
|
532
|
+
}
|
|
533
|
+
// Build WebhookRequest
|
|
534
|
+
const webhookRequest = {
|
|
535
|
+
method: req.method ?? 'POST',
|
|
536
|
+
url: url.toString(),
|
|
537
|
+
path: pathname,
|
|
538
|
+
headers: req.headers,
|
|
539
|
+
query: Object.fromEntries(url.searchParams.entries()),
|
|
540
|
+
body: parsedBody,
|
|
541
|
+
rawBody: rawBody ? Buffer.from(rawBody, 'utf-8') : undefined,
|
|
542
|
+
};
|
|
543
|
+
// Build WebhookContext
|
|
544
|
+
const webhookContext = {
|
|
545
|
+
env: process.env,
|
|
546
|
+
};
|
|
547
|
+
// Invoke the handler
|
|
548
|
+
let webhookResponse;
|
|
549
|
+
try {
|
|
550
|
+
webhookResponse = await webhookDef.handler(webhookRequest, webhookContext);
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
console.error(`Webhook handler '${handle}' error:`, err);
|
|
554
|
+
sendJSON(res, 500, { error: 'Webhook handler error' });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
// Send response
|
|
558
|
+
const status = webhookResponse.status ?? 200;
|
|
559
|
+
const responseHeaders = {
|
|
560
|
+
...webhookResponse.headers,
|
|
561
|
+
};
|
|
562
|
+
// Default to JSON content type if not specified
|
|
563
|
+
if (!responseHeaders['Content-Type'] && !responseHeaders['content-type']) {
|
|
564
|
+
responseHeaders['Content-Type'] = 'application/json';
|
|
565
|
+
}
|
|
566
|
+
res.writeHead(status, responseHeaders);
|
|
567
|
+
if (webhookResponse.body !== undefined) {
|
|
568
|
+
if (typeof webhookResponse.body === 'string') {
|
|
569
|
+
res.end(webhookResponse.body);
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
res.end(JSON.stringify(webhookResponse.body));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
res.end();
|
|
577
|
+
}
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
496
580
|
if (pathname === '/estimate' && req.method === 'POST') {
|
|
497
581
|
let estimateBody;
|
|
498
582
|
try {
|
|
@@ -657,7 +741,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
657
741
|
getHealthStatus: () => state.getHealthStatus(),
|
|
658
742
|
};
|
|
659
743
|
}
|
|
660
|
-
function createServerlessInstance(config, tools, callTool, state, mcpServer, registry) {
|
|
744
|
+
function createServerlessInstance(config, tools, callTool, state, mcpServer, registry, webhookRegistry) {
|
|
661
745
|
const headers = getDefaultHeaders(config.cors);
|
|
662
746
|
return {
|
|
663
747
|
async handler(event) {
|
|
@@ -667,6 +751,81 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
|
|
|
667
751
|
if (method === 'OPTIONS') {
|
|
668
752
|
return createResponse(200, { message: 'OK' }, headers);
|
|
669
753
|
}
|
|
754
|
+
// Handle webhook requests: /webhooks/{handle}
|
|
755
|
+
if (path.startsWith('/webhooks/') && webhookRegistry) {
|
|
756
|
+
const handle = path.slice('/webhooks/'.length);
|
|
757
|
+
const webhookDef = webhookRegistry[handle];
|
|
758
|
+
if (!webhookDef) {
|
|
759
|
+
return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
|
|
760
|
+
}
|
|
761
|
+
// Check if HTTP method is allowed
|
|
762
|
+
const allowedMethods = webhookDef.methods ?? ['POST'];
|
|
763
|
+
if (!allowedMethods.includes(method)) {
|
|
764
|
+
return createResponse(405, { error: `Method ${method} not allowed` }, headers);
|
|
765
|
+
}
|
|
766
|
+
// Get raw body
|
|
767
|
+
const rawBody = event.body ?? '';
|
|
768
|
+
// Parse body based on content type
|
|
769
|
+
let parsedBody;
|
|
770
|
+
const contentType = event.headers?.['content-type'] ?? event.headers?.['Content-Type'] ?? '';
|
|
771
|
+
if (contentType.includes('application/json')) {
|
|
772
|
+
try {
|
|
773
|
+
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
774
|
+
}
|
|
775
|
+
catch {
|
|
776
|
+
parsedBody = rawBody;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
parsedBody = rawBody;
|
|
781
|
+
}
|
|
782
|
+
// Build URL
|
|
783
|
+
const forwardedProto = event.headers?.['x-forwarded-proto'] ??
|
|
784
|
+
event.headers?.['X-Forwarded-Proto'];
|
|
785
|
+
const protocol = forwardedProto ?? 'https';
|
|
786
|
+
const host = event.headers?.host ?? event.headers?.Host ?? 'localhost';
|
|
787
|
+
const queryString = event.queryStringParameters
|
|
788
|
+
? '?' + new URLSearchParams(event.queryStringParameters).toString()
|
|
789
|
+
: '';
|
|
790
|
+
const webhookUrl = `${protocol}://${host}${path}${queryString}`;
|
|
791
|
+
// Build WebhookRequest
|
|
792
|
+
const webhookRequest = {
|
|
793
|
+
method,
|
|
794
|
+
url: webhookUrl,
|
|
795
|
+
path,
|
|
796
|
+
headers: event.headers,
|
|
797
|
+
query: event.queryStringParameters ?? {},
|
|
798
|
+
body: parsedBody,
|
|
799
|
+
rawBody: rawBody ? Buffer.from(rawBody, 'utf-8') : undefined,
|
|
800
|
+
};
|
|
801
|
+
// Build WebhookContext
|
|
802
|
+
const webhookContext = {
|
|
803
|
+
env: process.env,
|
|
804
|
+
};
|
|
805
|
+
// Invoke the handler
|
|
806
|
+
let webhookResponse;
|
|
807
|
+
try {
|
|
808
|
+
webhookResponse = await webhookDef.handler(webhookRequest, webhookContext);
|
|
809
|
+
}
|
|
810
|
+
catch (err) {
|
|
811
|
+
console.error(`Webhook handler '${handle}' error:`, err);
|
|
812
|
+
return createResponse(500, { error: 'Webhook handler error' }, headers);
|
|
813
|
+
}
|
|
814
|
+
// Build response headers
|
|
815
|
+
const responseHeaders = {
|
|
816
|
+
...headers,
|
|
817
|
+
...webhookResponse.headers,
|
|
818
|
+
};
|
|
819
|
+
const status = webhookResponse.status ?? 200;
|
|
820
|
+
const body = webhookResponse.body;
|
|
821
|
+
return {
|
|
822
|
+
statusCode: status,
|
|
823
|
+
headers: responseHeaders,
|
|
824
|
+
body: body !== undefined
|
|
825
|
+
? (typeof body === 'string' ? body : JSON.stringify(body))
|
|
826
|
+
: '',
|
|
827
|
+
};
|
|
828
|
+
}
|
|
670
829
|
if (path === '/core' && method === 'POST') {
|
|
671
830
|
let coreBody;
|
|
672
831
|
try {
|