skedyul 0.2.151 → 0.2.152
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/.build-stamp +1 -1
- package/dist/server.js +96 -61
- package/dist/types.d.ts +15 -1
- package/package.json +1 -1
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1770698146381
|
package/dist/server.js
CHANGED
|
@@ -690,6 +690,66 @@ function createSkedyulServer(config, registry, webhookRegistry) {
|
|
|
690
690
|
}
|
|
691
691
|
return createServerlessInstance(config, tools, callTool, state, mcpServer, registry, webhookRegistry);
|
|
692
692
|
}
|
|
693
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
694
|
+
// Shared Handler Helpers (used by both webhooks and OAuth callbacks)
|
|
695
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
696
|
+
/**
|
|
697
|
+
* Parses a handler envelope from the request body.
|
|
698
|
+
* Detects envelope format: { env: {...}, request: {...}, context?: {...} }
|
|
699
|
+
* Returns the extracted env and request, or null if not an envelope.
|
|
700
|
+
*/
|
|
701
|
+
function parseHandlerEnvelope(parsedBody) {
|
|
702
|
+
const isEnvelope = typeof parsedBody === 'object' &&
|
|
703
|
+
parsedBody !== null &&
|
|
704
|
+
'env' in parsedBody &&
|
|
705
|
+
'request' in parsedBody;
|
|
706
|
+
if (!isEnvelope) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
const envelope = parsedBody;
|
|
710
|
+
return {
|
|
711
|
+
env: envelope.env ?? {},
|
|
712
|
+
request: envelope.request,
|
|
713
|
+
context: envelope.context,
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Converts a raw HandlerRawRequest (wire format) to a rich WebhookRequest.
|
|
718
|
+
* Parses JSON body if content-type is application/json, creates Buffer rawBody.
|
|
719
|
+
*/
|
|
720
|
+
function buildRequestFromRaw(raw) {
|
|
721
|
+
// Parse the original request body
|
|
722
|
+
let parsedBody = raw.body;
|
|
723
|
+
const contentType = raw.headers['content-type'] ?? '';
|
|
724
|
+
if (contentType.includes('application/json')) {
|
|
725
|
+
try {
|
|
726
|
+
parsedBody = raw.body ? JSON.parse(raw.body) : {};
|
|
727
|
+
}
|
|
728
|
+
catch {
|
|
729
|
+
// Keep as string if JSON parsing fails
|
|
730
|
+
parsedBody = raw.body;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
method: raw.method,
|
|
735
|
+
url: raw.url,
|
|
736
|
+
path: raw.path,
|
|
737
|
+
headers: raw.headers,
|
|
738
|
+
query: raw.query,
|
|
739
|
+
body: parsedBody,
|
|
740
|
+
rawBody: raw.body ? Buffer.from(raw.body, 'utf-8') : undefined,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Builds request-scoped config by merging env from envelope with process.env fallbacks.
|
|
745
|
+
* Used for SKEDYUL_API_TOKEN and SKEDYUL_API_URL overrides.
|
|
746
|
+
*/
|
|
747
|
+
function buildRequestScopedConfig(env) {
|
|
748
|
+
return {
|
|
749
|
+
baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? '',
|
|
750
|
+
apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? '',
|
|
751
|
+
};
|
|
752
|
+
}
|
|
693
753
|
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer, webhookRegistry) {
|
|
694
754
|
const port = getListeningPort(config);
|
|
695
755
|
const httpServer = http_1.default.createServer(async (req, res) => {
|
|
@@ -742,49 +802,27 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
742
802
|
}
|
|
743
803
|
// Check if this is an envelope format from the platform
|
|
744
804
|
// Envelope format: { env: {...}, request: {...}, context: {...} }
|
|
745
|
-
const
|
|
746
|
-
parsedBody !== null &&
|
|
747
|
-
'env' in parsedBody &&
|
|
748
|
-
'request' in parsedBody &&
|
|
749
|
-
'context' in parsedBody);
|
|
805
|
+
const envelope = parseHandlerEnvelope(parsedBody);
|
|
750
806
|
let webhookRequest;
|
|
751
807
|
let webhookContext;
|
|
752
808
|
let requestEnv = {};
|
|
753
|
-
if (
|
|
754
|
-
// Platform envelope format -
|
|
755
|
-
const
|
|
756
|
-
requestEnv = envelope.env
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
try {
|
|
762
|
-
originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
|
|
763
|
-
}
|
|
764
|
-
catch {
|
|
765
|
-
// Keep as string if JSON parsing fails
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
webhookRequest = {
|
|
769
|
-
method: envelope.request.method,
|
|
770
|
-
url: envelope.request.url,
|
|
771
|
-
path: envelope.request.path,
|
|
772
|
-
headers: envelope.request.headers,
|
|
773
|
-
query: envelope.request.query,
|
|
774
|
-
body: originalParsedBody,
|
|
775
|
-
rawBody: envelope.request.body ? Buffer.from(envelope.request.body, 'utf-8') : undefined,
|
|
776
|
-
};
|
|
777
|
-
const envVars = { ...process.env, ...requestEnv };
|
|
778
|
-
const app = envelope.context.app;
|
|
809
|
+
if (envelope && 'context' in envelope && envelope.context) {
|
|
810
|
+
// Platform envelope format - use shared helpers
|
|
811
|
+
const context = envelope.context;
|
|
812
|
+
requestEnv = envelope.env;
|
|
813
|
+
// Convert raw request to rich request using shared helper
|
|
814
|
+
webhookRequest = buildRequestFromRaw(envelope.request);
|
|
815
|
+
const envVars = { ...process.env, ...envelope.env };
|
|
816
|
+
const app = context.app;
|
|
779
817
|
// Build webhook context based on whether we have installation context
|
|
780
|
-
if (
|
|
818
|
+
if (context.appInstallationId && context.workplace) {
|
|
781
819
|
// Runtime webhook context
|
|
782
820
|
webhookContext = {
|
|
783
821
|
env: envVars,
|
|
784
822
|
app,
|
|
785
|
-
appInstallationId:
|
|
786
|
-
workplace:
|
|
787
|
-
registration:
|
|
823
|
+
appInstallationId: context.appInstallationId,
|
|
824
|
+
workplace: context.workplace,
|
|
825
|
+
registration: context.registration ?? {},
|
|
788
826
|
};
|
|
789
827
|
}
|
|
790
828
|
else {
|
|
@@ -823,10 +861,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
823
861
|
Object.assign(process.env, requestEnv);
|
|
824
862
|
// Build request-scoped config for the skedyul client
|
|
825
863
|
// This uses AsyncLocalStorage to override the global config (same pattern as tools)
|
|
826
|
-
const requestConfig =
|
|
827
|
-
baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? '',
|
|
828
|
-
apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? '',
|
|
829
|
-
};
|
|
864
|
+
const requestConfig = buildRequestScopedConfig(requestEnv);
|
|
830
865
|
// Invoke the handler with request-scoped config
|
|
831
866
|
let webhookResponse;
|
|
832
867
|
try {
|
|
@@ -905,9 +940,9 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
905
940
|
sendJSON(res, 404, { error: 'OAuth callback handler not configured' });
|
|
906
941
|
return;
|
|
907
942
|
}
|
|
908
|
-
let
|
|
943
|
+
let parsedBody;
|
|
909
944
|
try {
|
|
910
|
-
|
|
945
|
+
parsedBody = await parseJSONBody(req);
|
|
911
946
|
}
|
|
912
947
|
catch {
|
|
913
948
|
sendJSON(res, 400, {
|
|
@@ -915,20 +950,20 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
915
950
|
});
|
|
916
951
|
return;
|
|
917
952
|
}
|
|
918
|
-
|
|
953
|
+
// Parse envelope using shared helper
|
|
954
|
+
const envelope = parseHandlerEnvelope(parsedBody);
|
|
955
|
+
if (!envelope) {
|
|
919
956
|
sendJSON(res, 400, {
|
|
920
|
-
error: { code: -32602, message: 'Missing
|
|
957
|
+
error: { code: -32602, message: 'Missing envelope format: expected { env, request }' },
|
|
921
958
|
});
|
|
922
959
|
return;
|
|
923
960
|
}
|
|
924
|
-
//
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
apiToken: oauthCallbackBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? '',
|
|
929
|
-
};
|
|
961
|
+
// Convert raw request to rich request using shared helper
|
|
962
|
+
const oauthRequest = buildRequestFromRaw(envelope.request);
|
|
963
|
+
// Build request-scoped config using shared helper
|
|
964
|
+
const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
|
|
930
965
|
const oauthCallbackContext = {
|
|
931
|
-
|
|
966
|
+
request: oauthRequest,
|
|
932
967
|
};
|
|
933
968
|
try {
|
|
934
969
|
const oauthCallbackHook = config.hooks.oauth_callback;
|
|
@@ -1576,24 +1611,24 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
|
|
|
1576
1611
|
if (!config.hooks?.oauth_callback) {
|
|
1577
1612
|
return createResponse(404, { error: 'OAuth callback handler not configured' }, headers);
|
|
1578
1613
|
}
|
|
1579
|
-
let
|
|
1614
|
+
let parsedBody;
|
|
1580
1615
|
try {
|
|
1581
|
-
|
|
1616
|
+
parsedBody = event.body ? JSON.parse(event.body) : {};
|
|
1582
1617
|
}
|
|
1583
1618
|
catch {
|
|
1584
1619
|
return createResponse(400, { error: { code: -32700, message: 'Parse error' } }, headers);
|
|
1585
1620
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1621
|
+
// Parse envelope using shared helper
|
|
1622
|
+
const envelope = parseHandlerEnvelope(parsedBody);
|
|
1623
|
+
if (!envelope) {
|
|
1624
|
+
return createResponse(400, { error: { code: -32602, message: 'Missing envelope format: expected { env, request }' } }, headers);
|
|
1588
1625
|
}
|
|
1589
|
-
//
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
apiToken: oauthCallbackBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? '',
|
|
1594
|
-
};
|
|
1626
|
+
// Convert raw request to rich request using shared helper
|
|
1627
|
+
const oauthRequest = buildRequestFromRaw(envelope.request);
|
|
1628
|
+
// Build request-scoped config using shared helper
|
|
1629
|
+
const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
|
|
1595
1630
|
const oauthCallbackContext = {
|
|
1596
|
-
|
|
1631
|
+
request: oauthRequest,
|
|
1597
1632
|
};
|
|
1598
1633
|
try {
|
|
1599
1634
|
const oauthCallbackHook = config.hooks.oauth_callback;
|
package/dist/types.d.ts
CHANGED
|
@@ -274,7 +274,8 @@ export type HasOAuthCallback<Hooks extends ServerHooks> = Hooks extends {
|
|
|
274
274
|
export type InstallHandlerResult<Hooks extends ServerHooks = ServerHooks> = HasOAuthCallback<Hooks> extends true ? InstallHandlerResponseOAuth : InstallHandlerResponseStandard;
|
|
275
275
|
export type InstallHandler<Hooks extends ServerHooks = ServerHooks> = (ctx: InstallHandlerContext) => Promise<InstallHandlerResult<Hooks>>;
|
|
276
276
|
export interface OAuthCallbackContext {
|
|
277
|
-
|
|
277
|
+
/** Full HTTP request from the OAuth provider */
|
|
278
|
+
request: WebhookRequest;
|
|
278
279
|
}
|
|
279
280
|
export interface OAuthCallbackResult {
|
|
280
281
|
env?: Record<string, string>;
|
|
@@ -322,6 +323,19 @@ export interface ServerlessServerInstance {
|
|
|
322
323
|
getHealthStatus(): HealthStatus;
|
|
323
324
|
}
|
|
324
325
|
export type SkedyulServerInstance = DedicatedServerInstance | ServerlessServerInstance;
|
|
326
|
+
/**
|
|
327
|
+
* Raw HTTP request shape sent over the wire in handler envelopes.
|
|
328
|
+
* This is the wire format used by both webhooks and OAuth callbacks.
|
|
329
|
+
* It gets converted to the rich WebhookRequest type at parse time.
|
|
330
|
+
*/
|
|
331
|
+
export interface HandlerRawRequest {
|
|
332
|
+
method: string;
|
|
333
|
+
url: string;
|
|
334
|
+
path: string;
|
|
335
|
+
headers: Record<string, string>;
|
|
336
|
+
query: Record<string, string>;
|
|
337
|
+
body: string;
|
|
338
|
+
}
|
|
325
339
|
/** Raw HTTP request received by webhooks */
|
|
326
340
|
export interface WebhookRequest {
|
|
327
341
|
method: string;
|