skedyul 1.2.32 → 1.2.34
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/cli/index.js +815 -921
- package/dist/dedicated/server.js +740 -846
- package/dist/esm/index.mjs +750 -856
- package/dist/index.js +750 -856
- package/dist/server/index.d.ts +2 -0
- package/dist/server/route-handlers/adapters.d.ts +37 -0
- package/dist/server/route-handlers/handlers.d.ts +63 -0
- package/dist/server/route-handlers/index.d.ts +10 -0
- package/dist/server/route-handlers/router.d.ts +13 -0
- package/dist/server/route-handlers/types.d.ts +75 -0
- package/dist/server.js +740 -846
- package/dist/serverless/server.mjs +740 -846
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2429,13 +2429,6 @@ function getDefaultHeaders(options) {
|
|
|
2429
2429
|
"Access-Control-Allow-Headers": options?.allowHeaders ?? "Content-Type"
|
|
2430
2430
|
};
|
|
2431
2431
|
}
|
|
2432
|
-
function createResponse(statusCode, body, headers) {
|
|
2433
|
-
return {
|
|
2434
|
-
statusCode,
|
|
2435
|
-
headers,
|
|
2436
|
-
body: JSON.stringify(body)
|
|
2437
|
-
};
|
|
2438
|
-
}
|
|
2439
2432
|
function getListeningPort(config) {
|
|
2440
2433
|
const envPort = Number.parseInt(process.env.PORT ?? "", 10);
|
|
2441
2434
|
if (!Number.isNaN(envPort)) {
|
|
@@ -2693,10 +2686,174 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
2693
2686
|
}
|
|
2694
2687
|
|
|
2695
2688
|
// src/server/dedicated.ts
|
|
2696
|
-
var fs7 = __toESM(require("fs"));
|
|
2697
2689
|
var import_http2 = __toESM(require("http"));
|
|
2698
2690
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
2699
2691
|
|
|
2692
|
+
// src/server/startup-logger.ts
|
|
2693
|
+
function padEnd(str, length) {
|
|
2694
|
+
if (str.length >= length) {
|
|
2695
|
+
return str.slice(0, length);
|
|
2696
|
+
}
|
|
2697
|
+
return str + " ".repeat(length - str.length);
|
|
2698
|
+
}
|
|
2699
|
+
function printStartupLog(config, tools, port) {
|
|
2700
|
+
if (process.env.NODE_ENV === "test") {
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
const webhookRegistry = config.webhooks;
|
|
2704
|
+
const webhookCount = webhookRegistry ? Object.keys(webhookRegistry).length : 0;
|
|
2705
|
+
const webhookNames = webhookRegistry ? Object.keys(webhookRegistry) : [];
|
|
2706
|
+
const maxRequests = config.maxRequests ?? parseNumberEnv(process.env.MCP_MAX_REQUESTS) ?? null;
|
|
2707
|
+
const ttlExtendSeconds = config.ttlExtendSeconds ?? parseNumberEnv(process.env.MCP_TTL_EXTEND) ?? 3600;
|
|
2708
|
+
const executableId = process.env.SKEDYUL_EXECUTABLE_ID || "local";
|
|
2709
|
+
const divider = "\u2550".repeat(70);
|
|
2710
|
+
const thinDivider = "\u2500".repeat(70);
|
|
2711
|
+
console.log("");
|
|
2712
|
+
console.log(`\u2554${divider}\u2557`);
|
|
2713
|
+
console.log(`\u2551 \u{1F680} Skedyul MCP Server Starting \u2551`);
|
|
2714
|
+
console.log(`\u2560${divider}\u2563`);
|
|
2715
|
+
console.log(`\u2551 \u2551`);
|
|
2716
|
+
console.log(`\u2551 \u{1F4E6} Server: ${padEnd(config.name, 49)}\u2551`);
|
|
2717
|
+
console.log(`\u2551 \u{1F3F7}\uFE0F Version: ${padEnd(config.version ?? "N/A", 49)}\u2551`);
|
|
2718
|
+
console.log(`\u2551 \u26A1 Compute: ${padEnd(config.computeLayer ?? "serverless", 49)}\u2551`);
|
|
2719
|
+
if (port) {
|
|
2720
|
+
console.log(`\u2551 \u{1F310} Port: ${padEnd(String(port), 49)}\u2551`);
|
|
2721
|
+
}
|
|
2722
|
+
console.log(`\u2551 \u{1F511} Executable: ${padEnd(executableId, 49)}\u2551`);
|
|
2723
|
+
console.log(`\u2551 \u2551`);
|
|
2724
|
+
console.log(`\u255F${thinDivider}\u2562`);
|
|
2725
|
+
console.log(`\u2551 \u2551`);
|
|
2726
|
+
console.log(`\u2551 \u{1F527} Tools (${tools.length}): \u2551`);
|
|
2727
|
+
const maxToolsToShow = 10;
|
|
2728
|
+
const toolsToShow = tools.slice(0, maxToolsToShow);
|
|
2729
|
+
for (const tool of toolsToShow) {
|
|
2730
|
+
console.log(`\u2551 \u2022 ${padEnd(tool.name, 61)}\u2551`);
|
|
2731
|
+
}
|
|
2732
|
+
if (tools.length > maxToolsToShow) {
|
|
2733
|
+
console.log(`\u2551 ... and ${tools.length - maxToolsToShow} more \u2551`);
|
|
2734
|
+
}
|
|
2735
|
+
if (webhookCount > 0) {
|
|
2736
|
+
console.log(`\u2551 \u2551`);
|
|
2737
|
+
console.log(`\u2551 \u{1FA9D} Webhooks (${webhookCount}): \u2551`);
|
|
2738
|
+
const maxWebhooksToShow = 5;
|
|
2739
|
+
const webhooksToShow = webhookNames.slice(0, maxWebhooksToShow);
|
|
2740
|
+
for (const name of webhooksToShow) {
|
|
2741
|
+
console.log(`\u2551 \u2022 /webhooks/${padEnd(name, 51)}\u2551`);
|
|
2742
|
+
}
|
|
2743
|
+
if (webhookCount > maxWebhooksToShow) {
|
|
2744
|
+
console.log(`\u2551 ... and ${webhookCount - maxWebhooksToShow} more \u2551`);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
console.log(`\u2551 \u2551`);
|
|
2748
|
+
console.log(`\u255F${thinDivider}\u2562`);
|
|
2749
|
+
console.log(`\u2551 \u2551`);
|
|
2750
|
+
console.log(`\u2551 \u2699\uFE0F Configuration: \u2551`);
|
|
2751
|
+
console.log(`\u2551 Max Requests: ${padEnd(maxRequests !== null ? String(maxRequests) : "unlimited", 46)}\u2551`);
|
|
2752
|
+
console.log(`\u2551 TTL Extend: ${padEnd(`${ttlExtendSeconds}s`, 46)}\u2551`);
|
|
2753
|
+
console.log(`\u2551 \u2551`);
|
|
2754
|
+
console.log(`\u255F${thinDivider}\u2562`);
|
|
2755
|
+
console.log(`\u2551 \u2705 Ready at ${padEnd((/* @__PURE__ */ new Date()).toISOString(), 55)}\u2551`);
|
|
2756
|
+
console.log(`\u255A${divider}\u255D`);
|
|
2757
|
+
console.log("");
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
// src/server/route-handlers/adapters.ts
|
|
2761
|
+
function fromLambdaEvent(event) {
|
|
2762
|
+
const path17 = event.path || event.rawPath || "/";
|
|
2763
|
+
const method = event.httpMethod || event.requestContext?.http?.method || "POST";
|
|
2764
|
+
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
2765
|
+
const protocol = forwardedProto ?? "https";
|
|
2766
|
+
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
2767
|
+
const queryString = event.queryStringParameters ? "?" + new URLSearchParams(
|
|
2768
|
+
event.queryStringParameters
|
|
2769
|
+
).toString() : "";
|
|
2770
|
+
const url = `${protocol}://${host}${path17}${queryString}`;
|
|
2771
|
+
return {
|
|
2772
|
+
path: path17,
|
|
2773
|
+
method,
|
|
2774
|
+
headers: event.headers,
|
|
2775
|
+
query: event.queryStringParameters ?? {},
|
|
2776
|
+
body: event.body,
|
|
2777
|
+
url
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
function toLambdaResponse(response, defaultHeaders) {
|
|
2781
|
+
return {
|
|
2782
|
+
statusCode: response.status,
|
|
2783
|
+
headers: {
|
|
2784
|
+
...defaultHeaders,
|
|
2785
|
+
...response.headers
|
|
2786
|
+
},
|
|
2787
|
+
body: response.body !== void 0 ? typeof response.body === "string" ? response.body : JSON.stringify(response.body) : ""
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
function parseJsonBody(req, errorCode = -32700, errorMessage = "Parse error") {
|
|
2791
|
+
try {
|
|
2792
|
+
const data = req.body ? JSON.parse(req.body) : {};
|
|
2793
|
+
return { success: true, data };
|
|
2794
|
+
} catch {
|
|
2795
|
+
return {
|
|
2796
|
+
success: false,
|
|
2797
|
+
error: {
|
|
2798
|
+
status: 400,
|
|
2799
|
+
body: {
|
|
2800
|
+
error: {
|
|
2801
|
+
code: errorCode,
|
|
2802
|
+
message: errorMessage
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
function parseJsonRpcBody(req) {
|
|
2810
|
+
try {
|
|
2811
|
+
const data = req.body ? JSON.parse(req.body) : {};
|
|
2812
|
+
return { success: true, data };
|
|
2813
|
+
} catch {
|
|
2814
|
+
return {
|
|
2815
|
+
success: false,
|
|
2816
|
+
error: {
|
|
2817
|
+
status: 400,
|
|
2818
|
+
body: {
|
|
2819
|
+
jsonrpc: "2.0",
|
|
2820
|
+
id: null,
|
|
2821
|
+
error: {
|
|
2822
|
+
code: -32700,
|
|
2823
|
+
message: "Parse error"
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
function getHeader(req, name) {
|
|
2831
|
+
const value = req.headers[name.toLowerCase()] ?? req.headers[name];
|
|
2832
|
+
if (Array.isArray(value)) {
|
|
2833
|
+
return value[0];
|
|
2834
|
+
}
|
|
2835
|
+
return value;
|
|
2836
|
+
}
|
|
2837
|
+
function getContentType(req) {
|
|
2838
|
+
return getHeader(req, "content-type") ?? "";
|
|
2839
|
+
}
|
|
2840
|
+
function parseBodyByContentType(req) {
|
|
2841
|
+
const contentType = getContentType(req);
|
|
2842
|
+
const rawBody = req.body ?? "";
|
|
2843
|
+
if (contentType.includes("application/json")) {
|
|
2844
|
+
try {
|
|
2845
|
+
return rawBody ? JSON.parse(rawBody) : {};
|
|
2846
|
+
} catch {
|
|
2847
|
+
return rawBody;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
return rawBody;
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
// src/server/route-handlers/handlers.ts
|
|
2854
|
+
var fs7 = __toESM(require("fs"));
|
|
2855
|
+
var path7 = __toESM(require("path"));
|
|
2856
|
+
|
|
2700
2857
|
// src/server/core-api-handler.ts
|
|
2701
2858
|
async function handleCoreMethod(method, params) {
|
|
2702
2859
|
const service = coreApiService.getService();
|
|
@@ -2840,74 +2997,6 @@ async function handleCoreMethod(method, params) {
|
|
|
2840
2997
|
};
|
|
2841
2998
|
}
|
|
2842
2999
|
|
|
2843
|
-
// src/server/startup-logger.ts
|
|
2844
|
-
function padEnd(str, length) {
|
|
2845
|
-
if (str.length >= length) {
|
|
2846
|
-
return str.slice(0, length);
|
|
2847
|
-
}
|
|
2848
|
-
return str + " ".repeat(length - str.length);
|
|
2849
|
-
}
|
|
2850
|
-
function printStartupLog(config, tools, port) {
|
|
2851
|
-
if (process.env.NODE_ENV === "test") {
|
|
2852
|
-
return;
|
|
2853
|
-
}
|
|
2854
|
-
const webhookRegistry = config.webhooks;
|
|
2855
|
-
const webhookCount = webhookRegistry ? Object.keys(webhookRegistry).length : 0;
|
|
2856
|
-
const webhookNames = webhookRegistry ? Object.keys(webhookRegistry) : [];
|
|
2857
|
-
const maxRequests = config.maxRequests ?? parseNumberEnv(process.env.MCP_MAX_REQUESTS) ?? null;
|
|
2858
|
-
const ttlExtendSeconds = config.ttlExtendSeconds ?? parseNumberEnv(process.env.MCP_TTL_EXTEND) ?? 3600;
|
|
2859
|
-
const executableId = process.env.SKEDYUL_EXECUTABLE_ID || "local";
|
|
2860
|
-
const divider = "\u2550".repeat(70);
|
|
2861
|
-
const thinDivider = "\u2500".repeat(70);
|
|
2862
|
-
console.log("");
|
|
2863
|
-
console.log(`\u2554${divider}\u2557`);
|
|
2864
|
-
console.log(`\u2551 \u{1F680} Skedyul MCP Server Starting \u2551`);
|
|
2865
|
-
console.log(`\u2560${divider}\u2563`);
|
|
2866
|
-
console.log(`\u2551 \u2551`);
|
|
2867
|
-
console.log(`\u2551 \u{1F4E6} Server: ${padEnd(config.name, 49)}\u2551`);
|
|
2868
|
-
console.log(`\u2551 \u{1F3F7}\uFE0F Version: ${padEnd(config.version ?? "N/A", 49)}\u2551`);
|
|
2869
|
-
console.log(`\u2551 \u26A1 Compute: ${padEnd(config.computeLayer ?? "serverless", 49)}\u2551`);
|
|
2870
|
-
if (port) {
|
|
2871
|
-
console.log(`\u2551 \u{1F310} Port: ${padEnd(String(port), 49)}\u2551`);
|
|
2872
|
-
}
|
|
2873
|
-
console.log(`\u2551 \u{1F511} Executable: ${padEnd(executableId, 49)}\u2551`);
|
|
2874
|
-
console.log(`\u2551 \u2551`);
|
|
2875
|
-
console.log(`\u255F${thinDivider}\u2562`);
|
|
2876
|
-
console.log(`\u2551 \u2551`);
|
|
2877
|
-
console.log(`\u2551 \u{1F527} Tools (${tools.length}): \u2551`);
|
|
2878
|
-
const maxToolsToShow = 10;
|
|
2879
|
-
const toolsToShow = tools.slice(0, maxToolsToShow);
|
|
2880
|
-
for (const tool of toolsToShow) {
|
|
2881
|
-
console.log(`\u2551 \u2022 ${padEnd(tool.name, 61)}\u2551`);
|
|
2882
|
-
}
|
|
2883
|
-
if (tools.length > maxToolsToShow) {
|
|
2884
|
-
console.log(`\u2551 ... and ${tools.length - maxToolsToShow} more \u2551`);
|
|
2885
|
-
}
|
|
2886
|
-
if (webhookCount > 0) {
|
|
2887
|
-
console.log(`\u2551 \u2551`);
|
|
2888
|
-
console.log(`\u2551 \u{1FA9D} Webhooks (${webhookCount}): \u2551`);
|
|
2889
|
-
const maxWebhooksToShow = 5;
|
|
2890
|
-
const webhooksToShow = webhookNames.slice(0, maxWebhooksToShow);
|
|
2891
|
-
for (const name of webhooksToShow) {
|
|
2892
|
-
console.log(`\u2551 \u2022 /webhooks/${padEnd(name, 51)}\u2551`);
|
|
2893
|
-
}
|
|
2894
|
-
if (webhookCount > maxWebhooksToShow) {
|
|
2895
|
-
console.log(`\u2551 ... and ${webhookCount - maxWebhooksToShow} more \u2551`);
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
console.log(`\u2551 \u2551`);
|
|
2899
|
-
console.log(`\u255F${thinDivider}\u2562`);
|
|
2900
|
-
console.log(`\u2551 \u2551`);
|
|
2901
|
-
console.log(`\u2551 \u2699\uFE0F Configuration: \u2551`);
|
|
2902
|
-
console.log(`\u2551 Max Requests: ${padEnd(maxRequests !== null ? String(maxRequests) : "unlimited", 46)}\u2551`);
|
|
2903
|
-
console.log(`\u2551 TTL Extend: ${padEnd(`${ttlExtendSeconds}s`, 46)}\u2551`);
|
|
2904
|
-
console.log(`\u2551 \u2551`);
|
|
2905
|
-
console.log(`\u255F${thinDivider}\u2562`);
|
|
2906
|
-
console.log(`\u2551 \u2705 Ready at ${padEnd((/* @__PURE__ */ new Date()).toISOString(), 55)}\u2551`);
|
|
2907
|
-
console.log(`\u255A${divider}\u255D`);
|
|
2908
|
-
console.log("");
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
3000
|
// src/server/config-serializer.ts
|
|
2912
3001
|
function serializeConfig(config) {
|
|
2913
3002
|
const registry = config.tools;
|
|
@@ -3226,7 +3315,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
|
|
|
3226
3315
|
}
|
|
3227
3316
|
|
|
3228
3317
|
// src/server/handlers/webhook-handler.ts
|
|
3229
|
-
function parseWebhookRequest(parsedBody, method, url,
|
|
3318
|
+
function parseWebhookRequest(parsedBody, method, url, path17, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
|
|
3230
3319
|
const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
|
|
3231
3320
|
if (isEnvelope) {
|
|
3232
3321
|
const envelope = parsedBody;
|
|
@@ -3280,7 +3369,7 @@ function parseWebhookRequest(parsedBody, method, url, path16, headers, query, ra
|
|
|
3280
3369
|
const webhookRequest = {
|
|
3281
3370
|
method,
|
|
3282
3371
|
url,
|
|
3283
|
-
path:
|
|
3372
|
+
path: path17,
|
|
3284
3373
|
headers,
|
|
3285
3374
|
query,
|
|
3286
3375
|
body: parsedBody,
|
|
@@ -3335,322 +3424,517 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
3335
3424
|
const allowedMethods = webhookDef.methods ?? ["POST"];
|
|
3336
3425
|
return allowedMethods.includes(method);
|
|
3337
3426
|
}
|
|
3338
|
-
|
|
3339
|
-
// src/server/
|
|
3340
|
-
|
|
3427
|
+
|
|
3428
|
+
// src/server/route-handlers/handlers.ts
|
|
3429
|
+
function getConfigFilePath() {
|
|
3430
|
+
return process.env.LAMBDA_TASK_ROOT ? path7.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
3431
|
+
}
|
|
3432
|
+
function handleHealthRoute(ctx) {
|
|
3433
|
+
return {
|
|
3434
|
+
status: 200,
|
|
3435
|
+
body: ctx.state.getHealthStatus()
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
function handleConfigRoute(ctx) {
|
|
3439
|
+
const configFilePath = getConfigFilePath();
|
|
3440
|
+
try {
|
|
3441
|
+
console.log(`[/config] Checking for config file at: ${configFilePath}`);
|
|
3442
|
+
if (fs7.existsSync(configFilePath)) {
|
|
3443
|
+
const fileConfig = JSON.parse(fs7.readFileSync(configFilePath, "utf-8"));
|
|
3444
|
+
console.log(
|
|
3445
|
+
`[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`
|
|
3446
|
+
);
|
|
3447
|
+
return { status: 200, body: fileConfig };
|
|
3448
|
+
}
|
|
3449
|
+
console.log("[/config] Config file not found, falling back to runtime serialization");
|
|
3450
|
+
} catch (err) {
|
|
3451
|
+
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3452
|
+
}
|
|
3453
|
+
const serialized = serializeConfig(ctx.config);
|
|
3454
|
+
console.log(
|
|
3455
|
+
`[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`
|
|
3456
|
+
);
|
|
3457
|
+
return { status: 200, body: serialized };
|
|
3458
|
+
}
|
|
3459
|
+
async function handleCoreRoute(req, ctx) {
|
|
3460
|
+
const parseResult = parseJsonBody(req);
|
|
3461
|
+
if (!parseResult.success) {
|
|
3462
|
+
return parseResult.error;
|
|
3463
|
+
}
|
|
3464
|
+
const coreBody = parseResult.data;
|
|
3465
|
+
if (!coreBody?.method) {
|
|
3466
|
+
return {
|
|
3467
|
+
status: 400,
|
|
3468
|
+
body: {
|
|
3469
|
+
error: {
|
|
3470
|
+
code: -32602,
|
|
3471
|
+
message: "Missing method"
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
const result = await handleCoreMethod(coreBody.method, coreBody.params);
|
|
3477
|
+
return { status: result.status, body: result.payload };
|
|
3478
|
+
}
|
|
3479
|
+
async function handleCoreWebhookRoute(req, ctx) {
|
|
3480
|
+
const rawBody = req.body ?? "";
|
|
3481
|
+
let webhookBody;
|
|
3482
|
+
try {
|
|
3483
|
+
webhookBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3484
|
+
} catch {
|
|
3485
|
+
return { status: 400, body: { status: "parse-error" } };
|
|
3486
|
+
}
|
|
3487
|
+
const coreWebhookRequest = {
|
|
3488
|
+
method: req.method,
|
|
3489
|
+
headers: req.headers,
|
|
3490
|
+
body: webhookBody,
|
|
3491
|
+
query: req.query,
|
|
3492
|
+
url: req.url,
|
|
3493
|
+
path: req.path,
|
|
3494
|
+
rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
|
|
3495
|
+
};
|
|
3496
|
+
const webhookResponse = await coreApiService.dispatchWebhook(coreWebhookRequest);
|
|
3497
|
+
return {
|
|
3498
|
+
status: webhookResponse.status,
|
|
3499
|
+
body: webhookResponse.body ?? {}
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
async function handleEstimateRoute(req, ctx) {
|
|
3503
|
+
const parseResult = parseJsonBody(req);
|
|
3504
|
+
if (!parseResult.success) {
|
|
3505
|
+
return parseResult.error;
|
|
3506
|
+
}
|
|
3507
|
+
const estimateBody = parseResult.data;
|
|
3508
|
+
try {
|
|
3509
|
+
const toolName = estimateBody.name;
|
|
3510
|
+
const toolArgs = estimateBody.inputs ?? {};
|
|
3511
|
+
let toolKey = null;
|
|
3512
|
+
let tool = null;
|
|
3513
|
+
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
3514
|
+
if (t.name === toolName || key === toolName) {
|
|
3515
|
+
toolKey = key;
|
|
3516
|
+
tool = t;
|
|
3517
|
+
break;
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
if (!tool || !toolKey) {
|
|
3521
|
+
return {
|
|
3522
|
+
status: 400,
|
|
3523
|
+
body: {
|
|
3524
|
+
error: {
|
|
3525
|
+
code: -32602,
|
|
3526
|
+
message: `Tool "${toolName}" not found`
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
3532
|
+
const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
3533
|
+
const estimateResponse = await ctx.callTool(toolKey, {
|
|
3534
|
+
inputs: validatedArgs,
|
|
3535
|
+
estimate: true
|
|
3536
|
+
});
|
|
3537
|
+
return {
|
|
3538
|
+
status: 200,
|
|
3539
|
+
body: {
|
|
3540
|
+
billing: estimateResponse.billing ?? { credits: 0 }
|
|
3541
|
+
}
|
|
3542
|
+
};
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
return {
|
|
3545
|
+
status: 500,
|
|
3546
|
+
body: {
|
|
3547
|
+
error: {
|
|
3548
|
+
code: -32603,
|
|
3549
|
+
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
async function handleInstallRoute(req, ctx) {
|
|
3556
|
+
const parseResult = parseJsonBody(req);
|
|
3557
|
+
if (!parseResult.success) {
|
|
3558
|
+
return parseResult.error;
|
|
3559
|
+
}
|
|
3560
|
+
const result = await handleInstall(parseResult.data, ctx.config.hooks);
|
|
3561
|
+
return { status: result.status, body: result.body };
|
|
3562
|
+
}
|
|
3563
|
+
async function handleUninstallRoute(req, ctx) {
|
|
3564
|
+
const parseResult = parseJsonBody(req);
|
|
3565
|
+
if (!parseResult.success) {
|
|
3566
|
+
return parseResult.error;
|
|
3567
|
+
}
|
|
3568
|
+
const result = await handleUninstall(parseResult.data, ctx.config.hooks);
|
|
3569
|
+
return { status: result.status, body: result.body };
|
|
3570
|
+
}
|
|
3571
|
+
async function handleProvisionRoute(req, ctx) {
|
|
3572
|
+
const parseResult = parseJsonBody(req);
|
|
3573
|
+
if (!parseResult.success) {
|
|
3574
|
+
return parseResult.error;
|
|
3575
|
+
}
|
|
3576
|
+
const result = await handleProvision(parseResult.data, ctx.config.hooks);
|
|
3577
|
+
return { status: result.status, body: result.body };
|
|
3578
|
+
}
|
|
3579
|
+
async function handleOAuthCallbackRoute(req, ctx) {
|
|
3580
|
+
const parseResult = parseJsonBody(req);
|
|
3581
|
+
if (!parseResult.success) {
|
|
3582
|
+
console.error("[OAuth Callback] Failed to parse JSON body");
|
|
3583
|
+
return parseResult.error;
|
|
3584
|
+
}
|
|
3585
|
+
const result = await handleOAuthCallback(parseResult.data, ctx.config.hooks);
|
|
3586
|
+
return { status: result.status, body: result.body };
|
|
3587
|
+
}
|
|
3588
|
+
async function handleWebhookRoute(req, handle, ctx) {
|
|
3589
|
+
if (!ctx.webhookRegistry) {
|
|
3590
|
+
return { status: 404, body: { error: `Webhook handler '${handle}' not found` } };
|
|
3591
|
+
}
|
|
3592
|
+
if (!ctx.webhookRegistry[handle]) {
|
|
3593
|
+
return { status: 404, body: { error: `Webhook handler '${handle}' not found` } };
|
|
3594
|
+
}
|
|
3595
|
+
if (!isMethodAllowed(ctx.webhookRegistry, handle, req.method)) {
|
|
3596
|
+
return { status: 405, body: { error: `Method ${req.method} not allowed` } };
|
|
3597
|
+
}
|
|
3598
|
+
const rawBody = req.body ?? "";
|
|
3599
|
+
const parsedBody = parseBodyByContentType(req);
|
|
3600
|
+
const parseResult = parseWebhookRequest(
|
|
3601
|
+
parsedBody,
|
|
3602
|
+
req.method,
|
|
3603
|
+
req.url,
|
|
3604
|
+
req.path,
|
|
3605
|
+
req.headers,
|
|
3606
|
+
req.query,
|
|
3607
|
+
rawBody,
|
|
3608
|
+
getHeader(req, "x-skedyul-app-id"),
|
|
3609
|
+
getHeader(req, "x-skedyul-app-version-id")
|
|
3610
|
+
);
|
|
3611
|
+
if ("error" in parseResult) {
|
|
3612
|
+
return { status: 400, body: { error: parseResult.error } };
|
|
3613
|
+
}
|
|
3614
|
+
const result = await executeWebhookHandler(handle, ctx.webhookRegistry, parseResult);
|
|
3615
|
+
return {
|
|
3616
|
+
status: result.status,
|
|
3617
|
+
body: result.body,
|
|
3618
|
+
headers: result.headers
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
async function handleMcpRoute(req, ctx) {
|
|
3622
|
+
const parseResult = parseJsonRpcBody(req);
|
|
3623
|
+
if (!parseResult.success) {
|
|
3624
|
+
return parseResult.error;
|
|
3625
|
+
}
|
|
3626
|
+
const body = parseResult.data;
|
|
3627
|
+
try {
|
|
3628
|
+
const { jsonrpc, id, method: rpcMethod, params } = body;
|
|
3629
|
+
if (jsonrpc !== "2.0") {
|
|
3630
|
+
return {
|
|
3631
|
+
status: 400,
|
|
3632
|
+
body: {
|
|
3633
|
+
jsonrpc: "2.0",
|
|
3634
|
+
id,
|
|
3635
|
+
error: {
|
|
3636
|
+
code: -32600,
|
|
3637
|
+
message: "Invalid Request"
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
let result;
|
|
3643
|
+
if (rpcMethod === "tools/list") {
|
|
3644
|
+
result = { tools: ctx.tools };
|
|
3645
|
+
} else if (rpcMethod === "tools/call") {
|
|
3646
|
+
return handleMcpToolsCall(params, id, ctx);
|
|
3647
|
+
} else if (rpcMethod === "webhooks/list") {
|
|
3648
|
+
const webhooks = ctx.webhookRegistry ? Object.values(ctx.webhookRegistry).map((w) => ({
|
|
3649
|
+
name: w.name,
|
|
3650
|
+
description: w.description,
|
|
3651
|
+
methods: w.methods ?? ["POST"],
|
|
3652
|
+
type: w.type ?? "WEBHOOK"
|
|
3653
|
+
})) : [];
|
|
3654
|
+
result = { webhooks };
|
|
3655
|
+
} else {
|
|
3656
|
+
return {
|
|
3657
|
+
status: 200,
|
|
3658
|
+
body: {
|
|
3659
|
+
jsonrpc: "2.0",
|
|
3660
|
+
id,
|
|
3661
|
+
error: {
|
|
3662
|
+
code: -32601,
|
|
3663
|
+
message: `Method not found: ${rpcMethod}`
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
return {
|
|
3669
|
+
status: 200,
|
|
3670
|
+
body: {
|
|
3671
|
+
jsonrpc: "2.0",
|
|
3672
|
+
id,
|
|
3673
|
+
result
|
|
3674
|
+
}
|
|
3675
|
+
};
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
return {
|
|
3678
|
+
status: 500,
|
|
3679
|
+
body: {
|
|
3680
|
+
jsonrpc: "2.0",
|
|
3681
|
+
id: body?.id ?? null,
|
|
3682
|
+
error: {
|
|
3683
|
+
code: -32603,
|
|
3684
|
+
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
async function handleMcpToolsCall(params, id, ctx) {
|
|
3691
|
+
const toolName = params?.name;
|
|
3692
|
+
const rawArgs = params?.arguments ?? {};
|
|
3693
|
+
console.log("[route-handlers /mcp] Received tools/call request:", JSON.stringify({
|
|
3694
|
+
toolName,
|
|
3695
|
+
hasArguments: !!params?.arguments,
|
|
3696
|
+
argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
|
|
3697
|
+
hasEnv: "env" in rawArgs,
|
|
3698
|
+
envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
|
|
3699
|
+
hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
|
|
3700
|
+
}, null, 2));
|
|
3701
|
+
const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
|
|
3702
|
+
const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
|
|
3703
|
+
const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
|
|
3704
|
+
const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
|
|
3705
|
+
const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
|
|
3706
|
+
console.log("[route-handlers /mcp] Extracted env:", JSON.stringify({
|
|
3707
|
+
hasSkedyulFormat,
|
|
3708
|
+
hasToolEnv: !!toolEnv,
|
|
3709
|
+
toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
3710
|
+
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
3711
|
+
}, null, 2));
|
|
3712
|
+
let toolKey = null;
|
|
3713
|
+
let tool = null;
|
|
3714
|
+
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
3715
|
+
if (t.name === toolName || key === toolName) {
|
|
3716
|
+
toolKey = key;
|
|
3717
|
+
tool = t;
|
|
3718
|
+
break;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
if (!tool || !toolKey) {
|
|
3722
|
+
return {
|
|
3723
|
+
status: 200,
|
|
3724
|
+
body: {
|
|
3725
|
+
jsonrpc: "2.0",
|
|
3726
|
+
id,
|
|
3727
|
+
error: {
|
|
3728
|
+
code: -32602,
|
|
3729
|
+
message: `Tool "${toolName}" not found`
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
};
|
|
3733
|
+
}
|
|
3734
|
+
try {
|
|
3735
|
+
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
3736
|
+
const outputSchema = getZodSchema3(tool.outputSchema);
|
|
3737
|
+
const hasOutputSchema = Boolean(outputSchema);
|
|
3738
|
+
const validatedInputs = inputSchema ? inputSchema.parse(toolInputs) : toolInputs;
|
|
3739
|
+
const toolResult = await ctx.callTool(toolKey, {
|
|
3740
|
+
inputs: validatedInputs,
|
|
3741
|
+
context: toolContext,
|
|
3742
|
+
env: toolEnv,
|
|
3743
|
+
invocation: toolInvocation
|
|
3744
|
+
});
|
|
3745
|
+
let result;
|
|
3746
|
+
if (toolResult.error) {
|
|
3747
|
+
const errorOutput = { error: toolResult.error };
|
|
3748
|
+
result = {
|
|
3749
|
+
content: [{ type: "text", text: JSON.stringify(errorOutput) }],
|
|
3750
|
+
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
3751
|
+
isError: true,
|
|
3752
|
+
billing: toolResult.billing
|
|
3753
|
+
};
|
|
3754
|
+
} else {
|
|
3755
|
+
const outputData = toolResult.output;
|
|
3756
|
+
let structuredContent;
|
|
3757
|
+
if (outputData) {
|
|
3758
|
+
structuredContent = { ...outputData, __effect: toolResult.effect };
|
|
3759
|
+
} else if (toolResult.effect) {
|
|
3760
|
+
structuredContent = { __effect: toolResult.effect };
|
|
3761
|
+
} else if (hasOutputSchema) {
|
|
3762
|
+
structuredContent = {};
|
|
3763
|
+
}
|
|
3764
|
+
result = {
|
|
3765
|
+
content: [{ type: "text", text: JSON.stringify(toolResult.output) }],
|
|
3766
|
+
structuredContent,
|
|
3767
|
+
billing: toolResult.billing
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3770
|
+
return {
|
|
3771
|
+
status: 200,
|
|
3772
|
+
body: {
|
|
3773
|
+
jsonrpc: "2.0",
|
|
3774
|
+
id,
|
|
3775
|
+
result
|
|
3776
|
+
}
|
|
3777
|
+
};
|
|
3778
|
+
} catch (validationError) {
|
|
3779
|
+
return {
|
|
3780
|
+
status: 200,
|
|
3781
|
+
body: {
|
|
3782
|
+
jsonrpc: "2.0",
|
|
3783
|
+
id,
|
|
3784
|
+
error: {
|
|
3785
|
+
code: -32602,
|
|
3786
|
+
message: validationError instanceof Error ? validationError.message : "Invalid arguments"
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
function createNotFoundResponse() {
|
|
3793
|
+
return {
|
|
3794
|
+
status: 404,
|
|
3795
|
+
body: {
|
|
3796
|
+
jsonrpc: "2.0",
|
|
3797
|
+
id: null,
|
|
3798
|
+
error: {
|
|
3799
|
+
code: -32601,
|
|
3800
|
+
message: "Not Found"
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
function createOptionsResponse() {
|
|
3806
|
+
return {
|
|
3807
|
+
status: 200,
|
|
3808
|
+
body: { message: "OK" }
|
|
3809
|
+
};
|
|
3810
|
+
}
|
|
3811
|
+
function createErrorResponse(err) {
|
|
3812
|
+
return {
|
|
3813
|
+
status: 500,
|
|
3814
|
+
body: {
|
|
3815
|
+
jsonrpc: "2.0",
|
|
3816
|
+
id: null,
|
|
3817
|
+
error: {
|
|
3818
|
+
code: -32603,
|
|
3819
|
+
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
};
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
// src/server/route-handlers/router.ts
|
|
3826
|
+
async function routeRequest(req, ctx) {
|
|
3827
|
+
try {
|
|
3828
|
+
if (req.method === "OPTIONS") {
|
|
3829
|
+
return createOptionsResponse();
|
|
3830
|
+
}
|
|
3831
|
+
if (req.path.startsWith("/webhooks/") && ctx.webhookRegistry) {
|
|
3832
|
+
const handle = req.path.slice("/webhooks/".length);
|
|
3833
|
+
return handleWebhookRoute(req, handle, ctx);
|
|
3834
|
+
}
|
|
3835
|
+
if (req.path === "/core" && req.method === "POST") {
|
|
3836
|
+
return handleCoreRoute(req, ctx);
|
|
3837
|
+
}
|
|
3838
|
+
if (req.path === "/core/webhook" && req.method === "POST") {
|
|
3839
|
+
return handleCoreWebhookRoute(req, ctx);
|
|
3840
|
+
}
|
|
3841
|
+
if (req.path === "/estimate" && req.method === "POST") {
|
|
3842
|
+
return handleEstimateRoute(req, ctx);
|
|
3843
|
+
}
|
|
3844
|
+
if (req.path === "/install" && req.method === "POST") {
|
|
3845
|
+
return handleInstallRoute(req, ctx);
|
|
3846
|
+
}
|
|
3847
|
+
if (req.path === "/uninstall" && req.method === "POST") {
|
|
3848
|
+
return handleUninstallRoute(req, ctx);
|
|
3849
|
+
}
|
|
3850
|
+
if (req.path === "/provision" && req.method === "POST") {
|
|
3851
|
+
return handleProvisionRoute(req, ctx);
|
|
3852
|
+
}
|
|
3853
|
+
if (req.path === "/oauth_callback" && req.method === "POST") {
|
|
3854
|
+
return handleOAuthCallbackRoute(req, ctx);
|
|
3855
|
+
}
|
|
3856
|
+
if (req.path === "/health" && req.method === "GET") {
|
|
3857
|
+
return handleHealthRoute(ctx);
|
|
3858
|
+
}
|
|
3859
|
+
if (req.path === "/config" && req.method === "GET") {
|
|
3860
|
+
return handleConfigRoute(ctx);
|
|
3861
|
+
}
|
|
3862
|
+
if (req.path === "/mcp" && req.method === "POST") {
|
|
3863
|
+
return handleMcpRoute(req, ctx);
|
|
3864
|
+
}
|
|
3865
|
+
return createNotFoundResponse();
|
|
3866
|
+
} catch (err) {
|
|
3867
|
+
return createErrorResponse(err);
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
// src/server/dedicated.ts
|
|
3872
|
+
function fromHttpRequest(req, url, rawBody) {
|
|
3873
|
+
return {
|
|
3874
|
+
path: url.pathname,
|
|
3875
|
+
method: req.method ?? "GET",
|
|
3876
|
+
headers: req.headers,
|
|
3877
|
+
query: Object.fromEntries(url.searchParams.entries()),
|
|
3878
|
+
body: rawBody,
|
|
3879
|
+
url: url.toString()
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
function sendUnifiedResponse(res, response, defaultHeaders) {
|
|
3883
|
+
const headers = {
|
|
3884
|
+
...defaultHeaders,
|
|
3885
|
+
...response.headers
|
|
3886
|
+
};
|
|
3887
|
+
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
3888
|
+
headers["Content-Type"] = "application/json";
|
|
3889
|
+
}
|
|
3890
|
+
res.writeHead(response.status, headers);
|
|
3891
|
+
if (response.body !== void 0) {
|
|
3892
|
+
if (typeof response.body === "string") {
|
|
3893
|
+
res.end(response.body);
|
|
3894
|
+
} else {
|
|
3895
|
+
res.end(JSON.stringify(response.body));
|
|
3896
|
+
}
|
|
3897
|
+
} else {
|
|
3898
|
+
res.end();
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3341
3901
|
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
|
|
3342
3902
|
const port = getListeningPort(config);
|
|
3903
|
+
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
3343
3904
|
const registry = config.tools;
|
|
3344
3905
|
const webhookRegistry = config.webhooks;
|
|
3906
|
+
const ctx = {
|
|
3907
|
+
config,
|
|
3908
|
+
tools,
|
|
3909
|
+
registry,
|
|
3910
|
+
webhookRegistry,
|
|
3911
|
+
callTool,
|
|
3912
|
+
state,
|
|
3913
|
+
mcpServer,
|
|
3914
|
+
defaultHeaders
|
|
3915
|
+
};
|
|
3345
3916
|
const httpServer = import_http2.default.createServer(
|
|
3346
3917
|
async (req, res) => {
|
|
3347
|
-
function sendCoreResult(result) {
|
|
3348
|
-
sendJSON(res, result.status, result.payload);
|
|
3349
|
-
}
|
|
3350
3918
|
try {
|
|
3351
3919
|
const url = new URL(
|
|
3352
3920
|
req.url || "/",
|
|
3353
3921
|
`http://${req.headers.host || "localhost"}`
|
|
3354
3922
|
);
|
|
3355
3923
|
const pathname = url.pathname;
|
|
3356
|
-
if (pathname === "/
|
|
3357
|
-
|
|
3358
|
-
return;
|
|
3359
|
-
}
|
|
3360
|
-
if (pathname === "/config" && req.method === "GET") {
|
|
3361
|
-
try {
|
|
3362
|
-
if (fs7.existsSync(CONFIG_FILE_PATH)) {
|
|
3363
|
-
const fileConfig = JSON.parse(fs7.readFileSync(CONFIG_FILE_PATH, "utf-8"));
|
|
3364
|
-
sendJSON(res, 200, fileConfig);
|
|
3365
|
-
return;
|
|
3366
|
-
}
|
|
3367
|
-
} catch (err) {
|
|
3368
|
-
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3369
|
-
}
|
|
3370
|
-
sendJSON(res, 200, serializeConfig(config));
|
|
3371
|
-
return;
|
|
3372
|
-
}
|
|
3373
|
-
if (pathname.startsWith("/webhooks/") && webhookRegistry) {
|
|
3374
|
-
const handle = pathname.slice("/webhooks/".length);
|
|
3375
|
-
if (!webhookRegistry[handle]) {
|
|
3376
|
-
sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
|
|
3377
|
-
return;
|
|
3378
|
-
}
|
|
3379
|
-
if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
|
|
3380
|
-
sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
|
|
3381
|
-
return;
|
|
3382
|
-
}
|
|
3383
|
-
let rawBody;
|
|
3384
|
-
try {
|
|
3385
|
-
rawBody = await readRawRequestBody(req);
|
|
3386
|
-
} catch {
|
|
3387
|
-
sendJSON(res, 400, { error: "Failed to read request body" });
|
|
3388
|
-
return;
|
|
3389
|
-
}
|
|
3390
|
-
let parsedBody;
|
|
3391
|
-
const contentType = req.headers["content-type"] ?? "";
|
|
3392
|
-
if (contentType.includes("application/json")) {
|
|
3393
|
-
try {
|
|
3394
|
-
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3395
|
-
} catch {
|
|
3396
|
-
parsedBody = rawBody;
|
|
3397
|
-
}
|
|
3398
|
-
} else {
|
|
3399
|
-
parsedBody = rawBody;
|
|
3400
|
-
}
|
|
3401
|
-
const parseResult = parseWebhookRequest(
|
|
3402
|
-
parsedBody,
|
|
3403
|
-
req.method ?? "POST",
|
|
3404
|
-
url.toString(),
|
|
3405
|
-
pathname,
|
|
3406
|
-
req.headers,
|
|
3407
|
-
Object.fromEntries(url.searchParams.entries()),
|
|
3408
|
-
rawBody,
|
|
3409
|
-
req.headers["x-skedyul-app-id"],
|
|
3410
|
-
req.headers["x-skedyul-app-version-id"]
|
|
3411
|
-
);
|
|
3412
|
-
if ("error" in parseResult) {
|
|
3413
|
-
sendJSON(res, 400, { error: parseResult.error });
|
|
3414
|
-
return;
|
|
3415
|
-
}
|
|
3416
|
-
const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
|
|
3417
|
-
const responseHeaders = {
|
|
3418
|
-
...result.headers
|
|
3419
|
-
};
|
|
3420
|
-
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
3421
|
-
responseHeaders["Content-Type"] = "application/json";
|
|
3422
|
-
}
|
|
3423
|
-
res.writeHead(result.status, responseHeaders);
|
|
3424
|
-
if (result.body !== void 0) {
|
|
3425
|
-
if (typeof result.body === "string") {
|
|
3426
|
-
res.end(result.body);
|
|
3427
|
-
} else {
|
|
3428
|
-
res.end(JSON.stringify(result.body));
|
|
3429
|
-
}
|
|
3430
|
-
} else {
|
|
3431
|
-
res.end();
|
|
3432
|
-
}
|
|
3433
|
-
return;
|
|
3434
|
-
}
|
|
3435
|
-
if (pathname === "/estimate" && req.method === "POST") {
|
|
3436
|
-
let estimateBody;
|
|
3437
|
-
try {
|
|
3438
|
-
estimateBody = await parseJSONBody(req);
|
|
3439
|
-
} catch {
|
|
3440
|
-
sendJSON(res, 400, {
|
|
3441
|
-
error: {
|
|
3442
|
-
code: -32700,
|
|
3443
|
-
message: "Parse error"
|
|
3444
|
-
}
|
|
3445
|
-
});
|
|
3446
|
-
return;
|
|
3447
|
-
}
|
|
3448
|
-
try {
|
|
3449
|
-
const estimateResponse = await callTool(estimateBody.name, {
|
|
3450
|
-
inputs: estimateBody.inputs,
|
|
3451
|
-
estimate: true
|
|
3452
|
-
});
|
|
3453
|
-
sendJSON(res, 200, {
|
|
3454
|
-
billing: estimateResponse.billing ?? { credits: 0 }
|
|
3455
|
-
});
|
|
3456
|
-
} catch (err) {
|
|
3457
|
-
sendJSON(res, 500, {
|
|
3458
|
-
error: {
|
|
3459
|
-
code: -32603,
|
|
3460
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3461
|
-
}
|
|
3462
|
-
});
|
|
3463
|
-
}
|
|
3464
|
-
return;
|
|
3465
|
-
}
|
|
3466
|
-
if (pathname === "/oauth_callback" && req.method === "POST") {
|
|
3467
|
-
let parsedBody;
|
|
3468
|
-
try {
|
|
3469
|
-
parsedBody = await parseJSONBody(req);
|
|
3470
|
-
} catch (err) {
|
|
3471
|
-
console.error("[OAuth Callback] Failed to parse JSON body:", err);
|
|
3472
|
-
sendJSON(res, 400, {
|
|
3473
|
-
error: { code: -32700, message: "Parse error" }
|
|
3474
|
-
});
|
|
3475
|
-
return;
|
|
3476
|
-
}
|
|
3477
|
-
const result = await handleOAuthCallback(parsedBody, config.hooks);
|
|
3478
|
-
sendJSON(res, result.status, result.body);
|
|
3479
|
-
return;
|
|
3480
|
-
}
|
|
3481
|
-
if (pathname === "/install" && req.method === "POST") {
|
|
3482
|
-
let installBody;
|
|
3483
|
-
try {
|
|
3484
|
-
installBody = await parseJSONBody(req);
|
|
3485
|
-
} catch {
|
|
3486
|
-
sendJSON(res, 400, {
|
|
3487
|
-
error: { code: -32700, message: "Parse error" }
|
|
3488
|
-
});
|
|
3489
|
-
return;
|
|
3490
|
-
}
|
|
3491
|
-
const result = await handleInstall(installBody, config.hooks);
|
|
3492
|
-
sendJSON(res, result.status, result.body);
|
|
3493
|
-
return;
|
|
3494
|
-
}
|
|
3495
|
-
if (pathname === "/uninstall" && req.method === "POST") {
|
|
3496
|
-
let uninstallBody;
|
|
3497
|
-
try {
|
|
3498
|
-
uninstallBody = await parseJSONBody(req);
|
|
3499
|
-
} catch {
|
|
3500
|
-
sendJSON(res, 400, {
|
|
3501
|
-
error: { code: -32700, message: "Parse error" }
|
|
3502
|
-
});
|
|
3503
|
-
return;
|
|
3504
|
-
}
|
|
3505
|
-
const result = await handleUninstall(uninstallBody, config.hooks);
|
|
3506
|
-
sendJSON(res, result.status, result.body);
|
|
3507
|
-
return;
|
|
3508
|
-
}
|
|
3509
|
-
if (pathname === "/provision" && req.method === "POST") {
|
|
3510
|
-
let provisionBody;
|
|
3511
|
-
try {
|
|
3512
|
-
provisionBody = await parseJSONBody(req);
|
|
3513
|
-
} catch {
|
|
3514
|
-
sendJSON(res, 400, {
|
|
3515
|
-
error: { code: -32700, message: "Parse error" }
|
|
3516
|
-
});
|
|
3517
|
-
return;
|
|
3518
|
-
}
|
|
3519
|
-
const result = await handleProvision(provisionBody, config.hooks);
|
|
3520
|
-
sendJSON(res, result.status, result.body);
|
|
3521
|
-
return;
|
|
3522
|
-
}
|
|
3523
|
-
if (pathname === "/core" && req.method === "POST") {
|
|
3524
|
-
let coreBody;
|
|
3525
|
-
try {
|
|
3526
|
-
coreBody = await parseJSONBody(req);
|
|
3527
|
-
} catch {
|
|
3528
|
-
sendJSON(res, 400, {
|
|
3529
|
-
error: {
|
|
3530
|
-
code: -32700,
|
|
3531
|
-
message: "Parse error"
|
|
3532
|
-
}
|
|
3533
|
-
});
|
|
3534
|
-
return;
|
|
3535
|
-
}
|
|
3536
|
-
if (!coreBody?.method) {
|
|
3537
|
-
sendJSON(res, 400, {
|
|
3538
|
-
error: {
|
|
3539
|
-
code: -32602,
|
|
3540
|
-
message: "Missing method"
|
|
3541
|
-
}
|
|
3542
|
-
});
|
|
3543
|
-
return;
|
|
3544
|
-
}
|
|
3545
|
-
const method = coreBody.method;
|
|
3546
|
-
const result = await handleCoreMethod(method, coreBody.params);
|
|
3547
|
-
sendCoreResult(result);
|
|
3548
|
-
return;
|
|
3549
|
-
}
|
|
3550
|
-
if (pathname === "/core/webhook" && req.method === "POST") {
|
|
3551
|
-
let rawWebhookBody;
|
|
3552
|
-
try {
|
|
3553
|
-
rawWebhookBody = await readRawRequestBody(req);
|
|
3554
|
-
} catch {
|
|
3555
|
-
sendJSON(res, 400, { status: "parse-error" });
|
|
3556
|
-
return;
|
|
3557
|
-
}
|
|
3558
|
-
let webhookBody;
|
|
3559
|
-
try {
|
|
3560
|
-
webhookBody = rawWebhookBody ? JSON.parse(rawWebhookBody) : {};
|
|
3561
|
-
} catch {
|
|
3562
|
-
sendJSON(res, 400, { status: "parse-error" });
|
|
3563
|
-
return;
|
|
3564
|
-
}
|
|
3565
|
-
const normalizedHeaders = Object.fromEntries(
|
|
3566
|
-
Object.entries(req.headers).map(([key, value]) => [
|
|
3567
|
-
key,
|
|
3568
|
-
typeof value === "string" ? value : value?.[0] ?? ""
|
|
3569
|
-
])
|
|
3570
|
-
);
|
|
3571
|
-
const coreWebhookRequest = {
|
|
3572
|
-
method: req.method ?? "POST",
|
|
3573
|
-
headers: normalizedHeaders,
|
|
3574
|
-
body: webhookBody,
|
|
3575
|
-
query: Object.fromEntries(url.searchParams.entries()),
|
|
3576
|
-
url: url.toString(),
|
|
3577
|
-
path: url.pathname,
|
|
3578
|
-
rawBody: rawWebhookBody ? Buffer.from(rawWebhookBody, "utf-8") : void 0
|
|
3579
|
-
};
|
|
3580
|
-
const webhookResponse = await coreApiService.dispatchWebhook(
|
|
3581
|
-
coreWebhookRequest
|
|
3582
|
-
);
|
|
3583
|
-
res.writeHead(webhookResponse.status, {
|
|
3584
|
-
"Content-Type": "application/json"
|
|
3585
|
-
});
|
|
3586
|
-
res.end(JSON.stringify(webhookResponse.body ?? {}));
|
|
3924
|
+
if (pathname === "/mcp" && req.method === "POST") {
|
|
3925
|
+
await handleMcpWithSdkTransport(req, res, url, ctx, mcpServer);
|
|
3587
3926
|
return;
|
|
3588
3927
|
}
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
method: body.method,
|
|
3595
|
-
toolName: body.params?.name,
|
|
3596
|
-
hasArguments: !!body.params?.arguments,
|
|
3597
|
-
argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
|
|
3598
|
-
hasEnv: !!body.params?.arguments?.env,
|
|
3599
|
-
envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
|
|
3600
|
-
hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
|
|
3601
|
-
}, null, 2));
|
|
3602
|
-
}
|
|
3603
|
-
if (body?.method === "tools/list") {
|
|
3604
|
-
sendJSON(res, 200, {
|
|
3605
|
-
jsonrpc: "2.0",
|
|
3606
|
-
id: body.id ?? null,
|
|
3607
|
-
result: { tools }
|
|
3608
|
-
});
|
|
3609
|
-
return;
|
|
3610
|
-
}
|
|
3611
|
-
if (body?.method === "webhooks/list") {
|
|
3612
|
-
const webhooks = webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
3613
|
-
name: w.name,
|
|
3614
|
-
description: w.description,
|
|
3615
|
-
methods: w.methods ?? ["POST"],
|
|
3616
|
-
type: w.type ?? "WEBHOOK"
|
|
3617
|
-
})) : [];
|
|
3618
|
-
sendJSON(res, 200, {
|
|
3619
|
-
jsonrpc: "2.0",
|
|
3620
|
-
id: body.id ?? null,
|
|
3621
|
-
result: { webhooks }
|
|
3622
|
-
});
|
|
3623
|
-
return;
|
|
3624
|
-
}
|
|
3625
|
-
const transport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
3626
|
-
sessionIdGenerator: void 0,
|
|
3627
|
-
enableJsonResponse: true
|
|
3628
|
-
});
|
|
3629
|
-
res.on("close", () => {
|
|
3630
|
-
transport.close();
|
|
3631
|
-
});
|
|
3632
|
-
await mcpServer.connect(transport);
|
|
3633
|
-
await transport.handleRequest(req, res, body);
|
|
3634
|
-
} catch (err) {
|
|
3635
|
-
sendJSON(res, 500, {
|
|
3636
|
-
jsonrpc: "2.0",
|
|
3637
|
-
id: null,
|
|
3638
|
-
error: {
|
|
3639
|
-
code: -32603,
|
|
3640
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3641
|
-
}
|
|
3642
|
-
});
|
|
3643
|
-
}
|
|
3928
|
+
let rawBody;
|
|
3929
|
+
try {
|
|
3930
|
+
rawBody = await readRawRequestBody(req);
|
|
3931
|
+
} catch {
|
|
3932
|
+
sendJSON(res, 400, { error: "Failed to read request body" });
|
|
3644
3933
|
return;
|
|
3645
3934
|
}
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
error: {
|
|
3650
|
-
code: -32601,
|
|
3651
|
-
message: "Not Found"
|
|
3652
|
-
}
|
|
3653
|
-
});
|
|
3935
|
+
const unifiedReq = fromHttpRequest(req, url, rawBody);
|
|
3936
|
+
const response = await routeRequest(unifiedReq, ctx);
|
|
3937
|
+
sendUnifiedResponse(res, response, defaultHeaders);
|
|
3654
3938
|
} catch (err) {
|
|
3655
3939
|
sendJSON(res, 500, {
|
|
3656
3940
|
jsonrpc: "2.0",
|
|
@@ -3677,485 +3961,95 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
3677
3961
|
getHealthStatus: () => state.getHealthStatus()
|
|
3678
3962
|
};
|
|
3679
3963
|
}
|
|
3964
|
+
async function handleMcpWithSdkTransport(req, res, url, ctx, mcpServer) {
|
|
3965
|
+
try {
|
|
3966
|
+
const body = await parseJSONBody(req);
|
|
3967
|
+
if (body?.method === "tools/call") {
|
|
3968
|
+
console.log(
|
|
3969
|
+
"[dedicated.ts /mcp] Received tools/call request:",
|
|
3970
|
+
JSON.stringify(
|
|
3971
|
+
{
|
|
3972
|
+
method: body.method,
|
|
3973
|
+
toolName: body.params?.name,
|
|
3974
|
+
hasArguments: !!body.params?.arguments,
|
|
3975
|
+
argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
|
|
3976
|
+
hasEnv: !!body.params?.arguments?.env,
|
|
3977
|
+
envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
|
|
3978
|
+
hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
|
|
3979
|
+
},
|
|
3980
|
+
null,
|
|
3981
|
+
2
|
|
3982
|
+
)
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
if (body?.method === "tools/list") {
|
|
3986
|
+
sendJSON(res, 200, {
|
|
3987
|
+
jsonrpc: "2.0",
|
|
3988
|
+
id: body.id ?? null,
|
|
3989
|
+
result: { tools: ctx.tools }
|
|
3990
|
+
});
|
|
3991
|
+
return;
|
|
3992
|
+
}
|
|
3993
|
+
if (body?.method === "webhooks/list") {
|
|
3994
|
+
const webhooks = ctx.webhookRegistry ? Object.values(ctx.webhookRegistry).map((w) => ({
|
|
3995
|
+
name: w.name,
|
|
3996
|
+
description: w.description,
|
|
3997
|
+
methods: w.methods ?? ["POST"],
|
|
3998
|
+
type: w.type ?? "WEBHOOK"
|
|
3999
|
+
})) : [];
|
|
4000
|
+
sendJSON(res, 200, {
|
|
4001
|
+
jsonrpc: "2.0",
|
|
4002
|
+
id: body.id ?? null,
|
|
4003
|
+
result: { webhooks }
|
|
4004
|
+
});
|
|
4005
|
+
return;
|
|
4006
|
+
}
|
|
4007
|
+
const transport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
4008
|
+
sessionIdGenerator: void 0,
|
|
4009
|
+
enableJsonResponse: true
|
|
4010
|
+
});
|
|
4011
|
+
res.on("close", () => {
|
|
4012
|
+
transport.close();
|
|
4013
|
+
});
|
|
4014
|
+
await mcpServer.connect(transport);
|
|
4015
|
+
await transport.handleRequest(req, res, body);
|
|
4016
|
+
} catch (err) {
|
|
4017
|
+
sendJSON(res, 500, {
|
|
4018
|
+
jsonrpc: "2.0",
|
|
4019
|
+
id: null,
|
|
4020
|
+
error: {
|
|
4021
|
+
code: -32603,
|
|
4022
|
+
message: err instanceof Error ? err.message : String(err ?? "")
|
|
4023
|
+
}
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
3680
4027
|
|
|
3681
4028
|
// src/server/serverless.ts
|
|
3682
|
-
var fs8 = __toESM(require("fs"));
|
|
3683
|
-
var CONFIG_FILE_PATH2 = ".skedyul/config.json";
|
|
3684
4029
|
function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
3685
|
-
const
|
|
4030
|
+
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
3686
4031
|
const registry = config.tools;
|
|
3687
4032
|
const webhookRegistry = config.webhooks;
|
|
3688
4033
|
let hasLoggedStartup = false;
|
|
4034
|
+
const ctx = {
|
|
4035
|
+
config,
|
|
4036
|
+
tools,
|
|
4037
|
+
registry,
|
|
4038
|
+
webhookRegistry,
|
|
4039
|
+
callTool,
|
|
4040
|
+
state,
|
|
4041
|
+
mcpServer,
|
|
4042
|
+
defaultHeaders
|
|
4043
|
+
};
|
|
3689
4044
|
return {
|
|
3690
4045
|
async handler(event) {
|
|
3691
4046
|
if (!hasLoggedStartup) {
|
|
3692
4047
|
printStartupLog(config, tools);
|
|
3693
4048
|
hasLoggedStartup = true;
|
|
3694
4049
|
}
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
if (method === "OPTIONS") {
|
|
3699
|
-
return createResponse(200, { message: "OK" }, headers);
|
|
3700
|
-
}
|
|
3701
|
-
if (path16.startsWith("/webhooks/") && webhookRegistry) {
|
|
3702
|
-
const handle = path16.slice("/webhooks/".length);
|
|
3703
|
-
if (!webhookRegistry[handle]) {
|
|
3704
|
-
return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
|
|
3705
|
-
}
|
|
3706
|
-
if (!isMethodAllowed(webhookRegistry, handle, method)) {
|
|
3707
|
-
return createResponse(405, { error: `Method ${method} not allowed` }, headers);
|
|
3708
|
-
}
|
|
3709
|
-
const rawBody = event.body ?? "";
|
|
3710
|
-
let parsedBody;
|
|
3711
|
-
const contentType = event.headers?.["content-type"] ?? event.headers?.["Content-Type"] ?? "";
|
|
3712
|
-
if (contentType.includes("application/json")) {
|
|
3713
|
-
try {
|
|
3714
|
-
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3715
|
-
} catch {
|
|
3716
|
-
parsedBody = rawBody;
|
|
3717
|
-
}
|
|
3718
|
-
} else {
|
|
3719
|
-
parsedBody = rawBody;
|
|
3720
|
-
}
|
|
3721
|
-
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
3722
|
-
const protocol = forwardedProto ?? "https";
|
|
3723
|
-
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3724
|
-
const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
|
|
3725
|
-
const webhookUrl = `${protocol}://${host}${path16}${queryString}`;
|
|
3726
|
-
const parseResult = parseWebhookRequest(
|
|
3727
|
-
parsedBody,
|
|
3728
|
-
method,
|
|
3729
|
-
webhookUrl,
|
|
3730
|
-
path16,
|
|
3731
|
-
event.headers,
|
|
3732
|
-
event.queryStringParameters ?? {},
|
|
3733
|
-
rawBody,
|
|
3734
|
-
event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
|
|
3735
|
-
event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
|
|
3736
|
-
);
|
|
3737
|
-
if ("error" in parseResult) {
|
|
3738
|
-
return createResponse(400, { error: parseResult.error }, headers);
|
|
3739
|
-
}
|
|
3740
|
-
const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
|
|
3741
|
-
const responseHeaders = {
|
|
3742
|
-
...headers,
|
|
3743
|
-
...result.headers
|
|
3744
|
-
};
|
|
3745
|
-
return {
|
|
3746
|
-
statusCode: result.status,
|
|
3747
|
-
headers: responseHeaders,
|
|
3748
|
-
body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
|
|
3749
|
-
};
|
|
3750
|
-
}
|
|
3751
|
-
if (path16 === "/core" && method === "POST") {
|
|
3752
|
-
let coreBody;
|
|
3753
|
-
try {
|
|
3754
|
-
coreBody = event.body ? JSON.parse(event.body) : {};
|
|
3755
|
-
} catch {
|
|
3756
|
-
return createResponse(
|
|
3757
|
-
400,
|
|
3758
|
-
{
|
|
3759
|
-
error: {
|
|
3760
|
-
code: -32700,
|
|
3761
|
-
message: "Parse error"
|
|
3762
|
-
}
|
|
3763
|
-
},
|
|
3764
|
-
headers
|
|
3765
|
-
);
|
|
3766
|
-
}
|
|
3767
|
-
if (!coreBody?.method) {
|
|
3768
|
-
return createResponse(
|
|
3769
|
-
400,
|
|
3770
|
-
{
|
|
3771
|
-
error: {
|
|
3772
|
-
code: -32602,
|
|
3773
|
-
message: "Missing method"
|
|
3774
|
-
}
|
|
3775
|
-
},
|
|
3776
|
-
headers
|
|
3777
|
-
);
|
|
3778
|
-
}
|
|
3779
|
-
const coreMethod = coreBody.method;
|
|
3780
|
-
const result = await handleCoreMethod(coreMethod, coreBody.params);
|
|
3781
|
-
return createResponse(result.status, result.payload, headers);
|
|
3782
|
-
}
|
|
3783
|
-
if (path16 === "/core/webhook" && method === "POST") {
|
|
3784
|
-
const rawWebhookBody = event.body ?? "";
|
|
3785
|
-
let webhookBody;
|
|
3786
|
-
try {
|
|
3787
|
-
webhookBody = rawWebhookBody ? JSON.parse(rawWebhookBody) : {};
|
|
3788
|
-
} catch {
|
|
3789
|
-
return createResponse(
|
|
3790
|
-
400,
|
|
3791
|
-
{ status: "parse-error" },
|
|
3792
|
-
headers
|
|
3793
|
-
);
|
|
3794
|
-
}
|
|
3795
|
-
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
3796
|
-
const protocol = forwardedProto ?? "https";
|
|
3797
|
-
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3798
|
-
const webhookUrl = `${protocol}://${host}${path16}`;
|
|
3799
|
-
const coreWebhookRequest = {
|
|
3800
|
-
method,
|
|
3801
|
-
headers: event.headers ?? {},
|
|
3802
|
-
body: webhookBody,
|
|
3803
|
-
query: event.queryStringParameters ?? {},
|
|
3804
|
-
url: webhookUrl,
|
|
3805
|
-
path: path16,
|
|
3806
|
-
rawBody: rawWebhookBody ? Buffer.from(rawWebhookBody, "utf-8") : void 0
|
|
3807
|
-
};
|
|
3808
|
-
const webhookResponse = await coreApiService.dispatchWebhook(
|
|
3809
|
-
coreWebhookRequest
|
|
3810
|
-
);
|
|
3811
|
-
return createResponse(
|
|
3812
|
-
webhookResponse.status,
|
|
3813
|
-
webhookResponse.body ?? {},
|
|
3814
|
-
headers
|
|
3815
|
-
);
|
|
3816
|
-
}
|
|
3817
|
-
if (path16 === "/estimate" && method === "POST") {
|
|
3818
|
-
let estimateBody;
|
|
3819
|
-
try {
|
|
3820
|
-
estimateBody = event.body ? JSON.parse(event.body) : {};
|
|
3821
|
-
} catch {
|
|
3822
|
-
return createResponse(
|
|
3823
|
-
400,
|
|
3824
|
-
{
|
|
3825
|
-
error: {
|
|
3826
|
-
code: -32700,
|
|
3827
|
-
message: "Parse error"
|
|
3828
|
-
}
|
|
3829
|
-
},
|
|
3830
|
-
headers
|
|
3831
|
-
);
|
|
3832
|
-
}
|
|
3833
|
-
try {
|
|
3834
|
-
const toolName = estimateBody.name;
|
|
3835
|
-
const toolArgs = estimateBody.inputs ?? {};
|
|
3836
|
-
let toolKey = null;
|
|
3837
|
-
let tool = null;
|
|
3838
|
-
for (const [key, t] of Object.entries(registry)) {
|
|
3839
|
-
if (t.name === toolName || key === toolName) {
|
|
3840
|
-
toolKey = key;
|
|
3841
|
-
tool = t;
|
|
3842
|
-
break;
|
|
3843
|
-
}
|
|
3844
|
-
}
|
|
3845
|
-
if (!tool || !toolKey) {
|
|
3846
|
-
return createResponse(
|
|
3847
|
-
400,
|
|
3848
|
-
{
|
|
3849
|
-
error: {
|
|
3850
|
-
code: -32602,
|
|
3851
|
-
message: `Tool "${toolName}" not found`
|
|
3852
|
-
}
|
|
3853
|
-
},
|
|
3854
|
-
headers
|
|
3855
|
-
);
|
|
3856
|
-
}
|
|
3857
|
-
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
3858
|
-
const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
3859
|
-
const estimateResponse = await callTool(toolKey, {
|
|
3860
|
-
inputs: validatedArgs,
|
|
3861
|
-
estimate: true
|
|
3862
|
-
});
|
|
3863
|
-
return createResponse(
|
|
3864
|
-
200,
|
|
3865
|
-
{
|
|
3866
|
-
billing: estimateResponse.billing ?? { credits: 0 }
|
|
3867
|
-
},
|
|
3868
|
-
headers
|
|
3869
|
-
);
|
|
3870
|
-
} catch (err) {
|
|
3871
|
-
return createResponse(
|
|
3872
|
-
500,
|
|
3873
|
-
{
|
|
3874
|
-
error: {
|
|
3875
|
-
code: -32603,
|
|
3876
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3877
|
-
}
|
|
3878
|
-
},
|
|
3879
|
-
headers
|
|
3880
|
-
);
|
|
3881
|
-
}
|
|
3882
|
-
}
|
|
3883
|
-
if (path16 === "/install" && method === "POST") {
|
|
3884
|
-
let installBody;
|
|
3885
|
-
try {
|
|
3886
|
-
installBody = event.body ? JSON.parse(event.body) : {};
|
|
3887
|
-
} catch {
|
|
3888
|
-
return createResponse(
|
|
3889
|
-
400,
|
|
3890
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3891
|
-
headers
|
|
3892
|
-
);
|
|
3893
|
-
}
|
|
3894
|
-
const result = await handleInstall(installBody, config.hooks);
|
|
3895
|
-
return createResponse(result.status, result.body, headers);
|
|
3896
|
-
}
|
|
3897
|
-
if (path16 === "/uninstall" && method === "POST") {
|
|
3898
|
-
let uninstallBody;
|
|
3899
|
-
try {
|
|
3900
|
-
uninstallBody = event.body ? JSON.parse(event.body) : {};
|
|
3901
|
-
} catch {
|
|
3902
|
-
return createResponse(
|
|
3903
|
-
400,
|
|
3904
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3905
|
-
headers
|
|
3906
|
-
);
|
|
3907
|
-
}
|
|
3908
|
-
const result = await handleUninstall(uninstallBody, config.hooks);
|
|
3909
|
-
return createResponse(result.status, result.body, headers);
|
|
3910
|
-
}
|
|
3911
|
-
if (path16 === "/provision" && method === "POST") {
|
|
3912
|
-
let provisionBody;
|
|
3913
|
-
try {
|
|
3914
|
-
provisionBody = event.body ? JSON.parse(event.body) : {};
|
|
3915
|
-
} catch {
|
|
3916
|
-
return createResponse(
|
|
3917
|
-
400,
|
|
3918
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3919
|
-
headers
|
|
3920
|
-
);
|
|
3921
|
-
}
|
|
3922
|
-
const result = await handleProvision(provisionBody, config.hooks);
|
|
3923
|
-
return createResponse(result.status, result.body, headers);
|
|
3924
|
-
}
|
|
3925
|
-
if (path16 === "/oauth_callback" && method === "POST") {
|
|
3926
|
-
let parsedBody;
|
|
3927
|
-
try {
|
|
3928
|
-
parsedBody = event.body ? JSON.parse(event.body) : {};
|
|
3929
|
-
} catch (err) {
|
|
3930
|
-
console.error("[OAuth Callback] Failed to parse JSON body:", err);
|
|
3931
|
-
return createResponse(
|
|
3932
|
-
400,
|
|
3933
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3934
|
-
headers
|
|
3935
|
-
);
|
|
3936
|
-
}
|
|
3937
|
-
const result = await handleOAuthCallback(parsedBody, config.hooks);
|
|
3938
|
-
return createResponse(result.status, result.body, headers);
|
|
3939
|
-
}
|
|
3940
|
-
if (path16 === "/health" && method === "GET") {
|
|
3941
|
-
return createResponse(200, state.getHealthStatus(), headers);
|
|
3942
|
-
}
|
|
3943
|
-
if (path16 === "/config" && method === "GET") {
|
|
3944
|
-
try {
|
|
3945
|
-
if (fs8.existsSync(CONFIG_FILE_PATH2)) {
|
|
3946
|
-
const fileConfig = JSON.parse(fs8.readFileSync(CONFIG_FILE_PATH2, "utf-8"));
|
|
3947
|
-
return createResponse(200, fileConfig, headers);
|
|
3948
|
-
}
|
|
3949
|
-
} catch (err) {
|
|
3950
|
-
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3951
|
-
}
|
|
3952
|
-
return createResponse(200, serializeConfig(config), headers);
|
|
3953
|
-
}
|
|
3954
|
-
if (path16 === "/mcp" && method === "POST") {
|
|
3955
|
-
let body;
|
|
3956
|
-
try {
|
|
3957
|
-
body = event.body ? JSON.parse(event.body) : {};
|
|
3958
|
-
} catch {
|
|
3959
|
-
return createResponse(
|
|
3960
|
-
400,
|
|
3961
|
-
{
|
|
3962
|
-
jsonrpc: "2.0",
|
|
3963
|
-
id: null,
|
|
3964
|
-
error: {
|
|
3965
|
-
code: -32700,
|
|
3966
|
-
message: "Parse error"
|
|
3967
|
-
}
|
|
3968
|
-
},
|
|
3969
|
-
headers
|
|
3970
|
-
);
|
|
3971
|
-
}
|
|
3972
|
-
try {
|
|
3973
|
-
const { jsonrpc, id, method: rpcMethod, params } = body;
|
|
3974
|
-
if (jsonrpc !== "2.0") {
|
|
3975
|
-
return createResponse(
|
|
3976
|
-
400,
|
|
3977
|
-
{
|
|
3978
|
-
jsonrpc: "2.0",
|
|
3979
|
-
id,
|
|
3980
|
-
error: {
|
|
3981
|
-
code: -32600,
|
|
3982
|
-
message: "Invalid Request"
|
|
3983
|
-
}
|
|
3984
|
-
},
|
|
3985
|
-
headers
|
|
3986
|
-
);
|
|
3987
|
-
}
|
|
3988
|
-
let result;
|
|
3989
|
-
if (rpcMethod === "tools/list") {
|
|
3990
|
-
result = { tools };
|
|
3991
|
-
} else if (rpcMethod === "tools/call") {
|
|
3992
|
-
const toolName = params?.name;
|
|
3993
|
-
const rawArgs = params?.arguments ?? {};
|
|
3994
|
-
console.log("[serverless.ts /mcp] Received tools/call request:", JSON.stringify({
|
|
3995
|
-
toolName,
|
|
3996
|
-
hasArguments: !!params?.arguments,
|
|
3997
|
-
argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
|
|
3998
|
-
hasEnv: "env" in rawArgs,
|
|
3999
|
-
envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
|
|
4000
|
-
hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
|
|
4001
|
-
}, null, 2));
|
|
4002
|
-
const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
|
|
4003
|
-
const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
|
|
4004
|
-
const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
|
|
4005
|
-
const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
|
|
4006
|
-
const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
|
|
4007
|
-
console.log("[serverless.ts /mcp] Extracted env:", JSON.stringify({
|
|
4008
|
-
hasSkedyulFormat,
|
|
4009
|
-
hasToolEnv: !!toolEnv,
|
|
4010
|
-
toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
4011
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
4012
|
-
}, null, 2));
|
|
4013
|
-
let toolKey = null;
|
|
4014
|
-
let tool = null;
|
|
4015
|
-
for (const [key, t] of Object.entries(registry)) {
|
|
4016
|
-
if (t.name === toolName || key === toolName) {
|
|
4017
|
-
toolKey = key;
|
|
4018
|
-
tool = t;
|
|
4019
|
-
break;
|
|
4020
|
-
}
|
|
4021
|
-
}
|
|
4022
|
-
if (!tool || !toolKey) {
|
|
4023
|
-
return createResponse(
|
|
4024
|
-
200,
|
|
4025
|
-
{
|
|
4026
|
-
jsonrpc: "2.0",
|
|
4027
|
-
id,
|
|
4028
|
-
error: {
|
|
4029
|
-
code: -32602,
|
|
4030
|
-
message: `Tool "${toolName}" not found`
|
|
4031
|
-
}
|
|
4032
|
-
},
|
|
4033
|
-
headers
|
|
4034
|
-
);
|
|
4035
|
-
}
|
|
4036
|
-
try {
|
|
4037
|
-
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
4038
|
-
const outputSchema = getZodSchema3(tool.outputSchema);
|
|
4039
|
-
const hasOutputSchema = Boolean(outputSchema);
|
|
4040
|
-
const validatedInputs = inputSchema ? inputSchema.parse(toolInputs) : toolInputs;
|
|
4041
|
-
const toolResult = await callTool(toolKey, {
|
|
4042
|
-
inputs: validatedInputs,
|
|
4043
|
-
context: toolContext,
|
|
4044
|
-
env: toolEnv,
|
|
4045
|
-
invocation: toolInvocation
|
|
4046
|
-
});
|
|
4047
|
-
if (toolResult.error) {
|
|
4048
|
-
const errorOutput = { error: toolResult.error };
|
|
4049
|
-
result = {
|
|
4050
|
-
content: [{ type: "text", text: JSON.stringify(errorOutput) }],
|
|
4051
|
-
// Don't provide structuredContent for error responses when tool has outputSchema
|
|
4052
|
-
// because the error response won't match the success schema and MCP SDK validates it
|
|
4053
|
-
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
4054
|
-
isError: true,
|
|
4055
|
-
billing: toolResult.billing
|
|
4056
|
-
};
|
|
4057
|
-
} else {
|
|
4058
|
-
const outputData = toolResult.output;
|
|
4059
|
-
let structuredContent;
|
|
4060
|
-
if (outputData) {
|
|
4061
|
-
structuredContent = { ...outputData, __effect: toolResult.effect };
|
|
4062
|
-
} else if (toolResult.effect) {
|
|
4063
|
-
structuredContent = { __effect: toolResult.effect };
|
|
4064
|
-
} else if (hasOutputSchema) {
|
|
4065
|
-
structuredContent = {};
|
|
4066
|
-
}
|
|
4067
|
-
result = {
|
|
4068
|
-
content: [{ type: "text", text: JSON.stringify(toolResult.output) }],
|
|
4069
|
-
structuredContent,
|
|
4070
|
-
billing: toolResult.billing
|
|
4071
|
-
};
|
|
4072
|
-
}
|
|
4073
|
-
} catch (validationError) {
|
|
4074
|
-
return createResponse(
|
|
4075
|
-
200,
|
|
4076
|
-
{
|
|
4077
|
-
jsonrpc: "2.0",
|
|
4078
|
-
id,
|
|
4079
|
-
error: {
|
|
4080
|
-
code: -32602,
|
|
4081
|
-
message: validationError instanceof Error ? validationError.message : "Invalid arguments"
|
|
4082
|
-
}
|
|
4083
|
-
},
|
|
4084
|
-
headers
|
|
4085
|
-
);
|
|
4086
|
-
}
|
|
4087
|
-
} else if (rpcMethod === "webhooks/list") {
|
|
4088
|
-
const webhooks = webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
4089
|
-
name: w.name,
|
|
4090
|
-
description: w.description,
|
|
4091
|
-
methods: w.methods ?? ["POST"],
|
|
4092
|
-
type: w.type ?? "WEBHOOK"
|
|
4093
|
-
})) : [];
|
|
4094
|
-
result = { webhooks };
|
|
4095
|
-
} else {
|
|
4096
|
-
return createResponse(
|
|
4097
|
-
200,
|
|
4098
|
-
{
|
|
4099
|
-
jsonrpc: "2.0",
|
|
4100
|
-
id,
|
|
4101
|
-
error: {
|
|
4102
|
-
code: -32601,
|
|
4103
|
-
message: `Method not found: ${rpcMethod}`
|
|
4104
|
-
}
|
|
4105
|
-
},
|
|
4106
|
-
headers
|
|
4107
|
-
);
|
|
4108
|
-
}
|
|
4109
|
-
return createResponse(
|
|
4110
|
-
200,
|
|
4111
|
-
{
|
|
4112
|
-
jsonrpc: "2.0",
|
|
4113
|
-
id,
|
|
4114
|
-
result
|
|
4115
|
-
},
|
|
4116
|
-
headers
|
|
4117
|
-
);
|
|
4118
|
-
} catch (err) {
|
|
4119
|
-
return createResponse(
|
|
4120
|
-
500,
|
|
4121
|
-
{
|
|
4122
|
-
jsonrpc: "2.0",
|
|
4123
|
-
id: body?.id ?? null,
|
|
4124
|
-
error: {
|
|
4125
|
-
code: -32603,
|
|
4126
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
4127
|
-
}
|
|
4128
|
-
},
|
|
4129
|
-
headers
|
|
4130
|
-
);
|
|
4131
|
-
}
|
|
4132
|
-
}
|
|
4133
|
-
return createResponse(
|
|
4134
|
-
404,
|
|
4135
|
-
{
|
|
4136
|
-
jsonrpc: "2.0",
|
|
4137
|
-
id: null,
|
|
4138
|
-
error: {
|
|
4139
|
-
code: -32601,
|
|
4140
|
-
message: "Not Found"
|
|
4141
|
-
}
|
|
4142
|
-
},
|
|
4143
|
-
headers
|
|
4144
|
-
);
|
|
4145
|
-
} catch (err) {
|
|
4146
|
-
return createResponse(
|
|
4147
|
-
500,
|
|
4148
|
-
{
|
|
4149
|
-
jsonrpc: "2.0",
|
|
4150
|
-
id: null,
|
|
4151
|
-
error: {
|
|
4152
|
-
code: -32603,
|
|
4153
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
4154
|
-
}
|
|
4155
|
-
},
|
|
4156
|
-
headers
|
|
4157
|
-
);
|
|
4158
|
-
}
|
|
4050
|
+
const req = fromLambdaEvent(event);
|
|
4051
|
+
const response = await routeRequest(req, ctx);
|
|
4052
|
+
return toLambdaResponse(response, defaultHeaders);
|
|
4159
4053
|
},
|
|
4160
4054
|
getHealthStatus: () => state.getHealthStatus()
|
|
4161
4055
|
};
|
|
@@ -4983,13 +4877,13 @@ Press Ctrl+C to stop`);
|
|
|
4983
4877
|
}
|
|
4984
4878
|
|
|
4985
4879
|
// src/cli/commands/validate.ts
|
|
4986
|
-
var
|
|
4987
|
-
var
|
|
4880
|
+
var fs9 = __toESM(require("fs"));
|
|
4881
|
+
var path10 = __toESM(require("path"));
|
|
4988
4882
|
init_utils();
|
|
4989
4883
|
|
|
4990
4884
|
// src/config/loader.ts
|
|
4991
|
-
var
|
|
4992
|
-
var
|
|
4885
|
+
var fs8 = __toESM(require("fs"));
|
|
4886
|
+
var path8 = __toESM(require("path"));
|
|
4993
4887
|
var os2 = __toESM(require("os"));
|
|
4994
4888
|
var CONFIG_FILE_NAMES = [
|
|
4995
4889
|
"skedyul.config.ts",
|
|
@@ -4998,13 +4892,13 @@ var CONFIG_FILE_NAMES = [
|
|
|
4998
4892
|
"skedyul.config.cjs"
|
|
4999
4893
|
];
|
|
5000
4894
|
async function transpileTypeScript(filePath) {
|
|
5001
|
-
const content =
|
|
5002
|
-
const configDir =
|
|
4895
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
4896
|
+
const configDir = path8.dirname(path8.resolve(filePath));
|
|
5003
4897
|
let transpiled = content.replace(/import\s+type\s+\{[^}]+\}\s+from\s+['"][^'"]+['"]\s*;?\n?/g, "").replace(/import\s+\{\s*defineConfig\s*\}\s+from\s+['"]skedyul['"]\s*;?\n?/g, "").replace(/:\s*SkedyulConfig/g, "").replace(/export\s+default\s+/, "module.exports = ").replace(/defineConfig\s*\(\s*\{/, "{").replace(/\}\s*\)\s*;?\s*$/, "}");
|
|
5004
4898
|
transpiled = transpiled.replace(
|
|
5005
4899
|
/import\s+(\w+)\s+from\s+['"](\.[^'"]+)['"]\s*(?:with\s*\{[^}]*\})?/g,
|
|
5006
4900
|
(match, varName, relativePath) => {
|
|
5007
|
-
const absolutePath =
|
|
4901
|
+
const absolutePath = path8.resolve(configDir, relativePath);
|
|
5008
4902
|
return `const ${varName} = require('${absolutePath.replace(/\\/g, "/")}')`;
|
|
5009
4903
|
}
|
|
5010
4904
|
);
|
|
@@ -5012,8 +4906,8 @@ async function transpileTypeScript(filePath) {
|
|
|
5012
4906
|
return transpiled;
|
|
5013
4907
|
}
|
|
5014
4908
|
async function loadConfig(configPath) {
|
|
5015
|
-
const absolutePath =
|
|
5016
|
-
if (!
|
|
4909
|
+
const absolutePath = path8.resolve(configPath);
|
|
4910
|
+
if (!fs8.existsSync(absolutePath)) {
|
|
5017
4911
|
throw new Error(`Config file not found: ${absolutePath}`);
|
|
5018
4912
|
}
|
|
5019
4913
|
const isTypeScript = absolutePath.endsWith(".ts");
|
|
@@ -5022,8 +4916,8 @@ async function loadConfig(configPath) {
|
|
|
5022
4916
|
if (isTypeScript) {
|
|
5023
4917
|
const transpiled = await transpileTypeScript(absolutePath);
|
|
5024
4918
|
const tempDir = os2.tmpdir();
|
|
5025
|
-
const tempFile =
|
|
5026
|
-
|
|
4919
|
+
const tempFile = path8.join(tempDir, `skedyul-config-${Date.now()}.cjs`);
|
|
4920
|
+
fs8.writeFileSync(tempFile, transpiled);
|
|
5027
4921
|
moduleToLoad = tempFile;
|
|
5028
4922
|
try {
|
|
5029
4923
|
const module3 = require(moduleToLoad);
|
|
@@ -5037,7 +4931,7 @@ async function loadConfig(configPath) {
|
|
|
5037
4931
|
return config2;
|
|
5038
4932
|
} finally {
|
|
5039
4933
|
try {
|
|
5040
|
-
|
|
4934
|
+
fs8.unlinkSync(tempFile);
|
|
5041
4935
|
} catch {
|
|
5042
4936
|
}
|
|
5043
4937
|
}
|
|
@@ -5069,9 +4963,9 @@ function validateConfig(config) {
|
|
|
5069
4963
|
}
|
|
5070
4964
|
|
|
5071
4965
|
// src/config/resolver.ts
|
|
5072
|
-
var
|
|
4966
|
+
var path9 = __toESM(require("path"));
|
|
5073
4967
|
async function loadAndResolveConfig(configPath) {
|
|
5074
|
-
const absolutePath =
|
|
4968
|
+
const absolutePath = path9.resolve(configPath);
|
|
5075
4969
|
const module2 = await import(absolutePath);
|
|
5076
4970
|
const config = module2.default;
|
|
5077
4971
|
if (!config || typeof config !== "object") {
|
|
@@ -5174,8 +5068,8 @@ Examples:
|
|
|
5174
5068
|
}
|
|
5175
5069
|
function findConfigFile2(startDir) {
|
|
5176
5070
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5177
|
-
const filePath =
|
|
5178
|
-
if (
|
|
5071
|
+
const filePath = path10.join(startDir, fileName);
|
|
5072
|
+
if (fs9.existsSync(filePath)) {
|
|
5179
5073
|
return filePath;
|
|
5180
5074
|
}
|
|
5181
5075
|
}
|
|
@@ -5209,9 +5103,9 @@ async function validateCommand(args2) {
|
|
|
5209
5103
|
}
|
|
5210
5104
|
configPath = foundConfig;
|
|
5211
5105
|
} else {
|
|
5212
|
-
configPath =
|
|
5106
|
+
configPath = path10.resolve(process.cwd(), configPath);
|
|
5213
5107
|
}
|
|
5214
|
-
if (!
|
|
5108
|
+
if (!fs9.existsSync(configPath)) {
|
|
5215
5109
|
const result2 = {
|
|
5216
5110
|
valid: false,
|
|
5217
5111
|
configPath,
|
|
@@ -5256,8 +5150,8 @@ async function validateCommand(args2) {
|
|
|
5256
5150
|
if (provision?.workflows) {
|
|
5257
5151
|
for (const workflow of provision.workflows) {
|
|
5258
5152
|
if (workflow.path) {
|
|
5259
|
-
const absoluteWorkflowPath =
|
|
5260
|
-
if (!
|
|
5153
|
+
const absoluteWorkflowPath = path10.resolve(path10.dirname(configPath), workflow.path);
|
|
5154
|
+
if (!fs9.existsSync(absoluteWorkflowPath)) {
|
|
5261
5155
|
warnings.push(`Workflow file not found: ${workflow.path}`);
|
|
5262
5156
|
}
|
|
5263
5157
|
}
|
|
@@ -5352,8 +5246,8 @@ async function validateCommand(args2) {
|
|
|
5352
5246
|
}
|
|
5353
5247
|
|
|
5354
5248
|
// src/cli/commands/diff.ts
|
|
5355
|
-
var
|
|
5356
|
-
var
|
|
5249
|
+
var fs10 = __toESM(require("fs"));
|
|
5250
|
+
var path11 = __toESM(require("path"));
|
|
5357
5251
|
init_utils();
|
|
5358
5252
|
function printHelp6() {
|
|
5359
5253
|
console.log(`
|
|
@@ -5386,8 +5280,8 @@ Examples:
|
|
|
5386
5280
|
}
|
|
5387
5281
|
function findConfigFile3(startDir) {
|
|
5388
5282
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5389
|
-
const filePath =
|
|
5390
|
-
if (
|
|
5283
|
+
const filePath = path11.join(startDir, fileName);
|
|
5284
|
+
if (fs10.existsSync(filePath)) {
|
|
5391
5285
|
return filePath;
|
|
5392
5286
|
}
|
|
5393
5287
|
}
|
|
@@ -5443,9 +5337,9 @@ async function diffCommand(args2) {
|
|
|
5443
5337
|
}
|
|
5444
5338
|
configPath = foundConfig;
|
|
5445
5339
|
} else {
|
|
5446
|
-
configPath =
|
|
5340
|
+
configPath = path11.resolve(process.cwd(), configPath);
|
|
5447
5341
|
}
|
|
5448
|
-
if (!
|
|
5342
|
+
if (!fs10.existsSync(configPath)) {
|
|
5449
5343
|
if (jsonOutput) {
|
|
5450
5344
|
console.log(JSON.stringify({ error: `Config file not found: ${configPath}` }));
|
|
5451
5345
|
} else {
|
|
@@ -5487,7 +5381,7 @@ async function diffCommand(args2) {
|
|
|
5487
5381
|
const registryPath = flags.registry || flags.r;
|
|
5488
5382
|
if (registryPath) {
|
|
5489
5383
|
try {
|
|
5490
|
-
const registry = await loadRegistry(
|
|
5384
|
+
const registry = await loadRegistry(path11.resolve(process.cwd(), registryPath));
|
|
5491
5385
|
const toolNames = Object.values(registry).map((t) => t.name);
|
|
5492
5386
|
toolsDiff = {
|
|
5493
5387
|
added: toolNames,
|
|
@@ -5560,8 +5454,8 @@ async function diffCommand(args2) {
|
|
|
5560
5454
|
}
|
|
5561
5455
|
|
|
5562
5456
|
// src/cli/commands/deploy.ts
|
|
5563
|
-
var
|
|
5564
|
-
var
|
|
5457
|
+
var fs11 = __toESM(require("fs"));
|
|
5458
|
+
var path12 = __toESM(require("path"));
|
|
5565
5459
|
var readline2 = __toESM(require("readline"));
|
|
5566
5460
|
init_utils();
|
|
5567
5461
|
function printHelp7() {
|
|
@@ -5597,8 +5491,8 @@ Examples:
|
|
|
5597
5491
|
}
|
|
5598
5492
|
function findConfigFile4(startDir) {
|
|
5599
5493
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5600
|
-
const filePath =
|
|
5601
|
-
if (
|
|
5494
|
+
const filePath = path12.join(startDir, fileName);
|
|
5495
|
+
if (fs11.existsSync(filePath)) {
|
|
5602
5496
|
return filePath;
|
|
5603
5497
|
}
|
|
5604
5498
|
}
|
|
@@ -5712,9 +5606,9 @@ async function deployCommand(args2) {
|
|
|
5712
5606
|
}
|
|
5713
5607
|
configPath = foundConfig;
|
|
5714
5608
|
} else {
|
|
5715
|
-
configPath =
|
|
5609
|
+
configPath = path12.resolve(process.cwd(), configPath);
|
|
5716
5610
|
}
|
|
5717
|
-
if (!
|
|
5611
|
+
if (!fs11.existsSync(configPath)) {
|
|
5718
5612
|
if (jsonOutput) {
|
|
5719
5613
|
console.log(JSON.stringify({ error: `Config file not found: ${configPath}` }));
|
|
5720
5614
|
} else {
|
|
@@ -6031,8 +5925,8 @@ async function logoutCommand(args2) {
|
|
|
6031
5925
|
}
|
|
6032
5926
|
|
|
6033
5927
|
// src/cli/commands/auth/status.ts
|
|
6034
|
-
var
|
|
6035
|
-
var
|
|
5928
|
+
var fs12 = __toESM(require("fs"));
|
|
5929
|
+
var path13 = __toESM(require("path"));
|
|
6036
5930
|
init_utils();
|
|
6037
5931
|
function printHelp10() {
|
|
6038
5932
|
console.log(`
|
|
@@ -6106,17 +6000,17 @@ async function statusCommand(args2) {
|
|
|
6106
6000
|
console.log("");
|
|
6107
6001
|
console.log(" * = active profile");
|
|
6108
6002
|
}
|
|
6109
|
-
const linksDir =
|
|
6003
|
+
const linksDir = path13.join(process.cwd(), ".skedyul", "links");
|
|
6110
6004
|
console.log("");
|
|
6111
6005
|
console.log("LINKED WORKPLACES (this project)");
|
|
6112
6006
|
console.log("\u2500".repeat(60));
|
|
6113
|
-
if (
|
|
6114
|
-
const linkFiles =
|
|
6007
|
+
if (fs12.existsSync(linksDir)) {
|
|
6008
|
+
const linkFiles = fs12.readdirSync(linksDir).filter((f) => f.endsWith(".json"));
|
|
6115
6009
|
if (linkFiles.length > 0) {
|
|
6116
6010
|
for (const file2 of linkFiles) {
|
|
6117
6011
|
const subdomain = file2.replace(".json", "");
|
|
6118
6012
|
try {
|
|
6119
|
-
const content =
|
|
6013
|
+
const content = fs12.readFileSync(path13.join(linksDir, file2), "utf-8");
|
|
6120
6014
|
const link = JSON.parse(content);
|
|
6121
6015
|
console.log(` - ${subdomain} (${link.appHandle})`);
|
|
6122
6016
|
} catch {
|
|
@@ -6473,8 +6367,8 @@ EXAMPLES
|
|
|
6473
6367
|
}
|
|
6474
6368
|
|
|
6475
6369
|
// src/cli/commands/config-export.ts
|
|
6476
|
-
var
|
|
6477
|
-
var
|
|
6370
|
+
var fs13 = __toESM(require("fs"));
|
|
6371
|
+
var path14 = __toESM(require("path"));
|
|
6478
6372
|
function printHelp13() {
|
|
6479
6373
|
console.log(`
|
|
6480
6374
|
SKEDYUL CONFIG:EXPORT - Export resolved config to JSON
|
|
@@ -6520,8 +6414,8 @@ async function configExportCommand(args2) {
|
|
|
6520
6414
|
const cwd = process.cwd();
|
|
6521
6415
|
let configPath = null;
|
|
6522
6416
|
for (const name of CONFIG_FILE_NAMES) {
|
|
6523
|
-
const testPath =
|
|
6524
|
-
if (
|
|
6417
|
+
const testPath = path14.join(cwd, name);
|
|
6418
|
+
if (fs13.existsSync(testPath)) {
|
|
6525
6419
|
configPath = testPath;
|
|
6526
6420
|
break;
|
|
6527
6421
|
}
|
|
@@ -6531,16 +6425,16 @@ async function configExportCommand(args2) {
|
|
|
6531
6425
|
console.error("Make sure you are in the root of your integration project.");
|
|
6532
6426
|
process.exit(1);
|
|
6533
6427
|
}
|
|
6534
|
-
console.log(`Loading config from ${
|
|
6428
|
+
console.log(`Loading config from ${path14.basename(configPath)}...`);
|
|
6535
6429
|
try {
|
|
6536
6430
|
const resolvedConfig = await loadAndResolveConfig(configPath);
|
|
6537
6431
|
const serialized = serializeResolvedConfig(resolvedConfig);
|
|
6538
|
-
const outputDir =
|
|
6539
|
-
if (!
|
|
6540
|
-
|
|
6432
|
+
const outputDir = path14.dirname(path14.resolve(cwd, outputPath));
|
|
6433
|
+
if (!fs13.existsSync(outputDir)) {
|
|
6434
|
+
fs13.mkdirSync(outputDir, { recursive: true });
|
|
6541
6435
|
}
|
|
6542
|
-
const fullOutputPath =
|
|
6543
|
-
|
|
6436
|
+
const fullOutputPath = path14.resolve(cwd, outputPath);
|
|
6437
|
+
fs13.writeFileSync(fullOutputPath, JSON.stringify(serialized, null, 2), "utf-8");
|
|
6544
6438
|
console.log(``);
|
|
6545
6439
|
console.log(`Config exported successfully!`);
|
|
6546
6440
|
console.log(` Output: ${outputPath}`);
|
|
@@ -7123,8 +7017,8 @@ async function handleCreateMany(modelHandle, flags) {
|
|
|
7123
7017
|
console.error("Usage: skedyul instances create-many <model> --file data.json --workplace <subdomain>");
|
|
7124
7018
|
process.exit(1);
|
|
7125
7019
|
}
|
|
7126
|
-
const
|
|
7127
|
-
const fileContent =
|
|
7020
|
+
const fs16 = await import("fs");
|
|
7021
|
+
const fileContent = fs16.readFileSync(filePath, "utf-8");
|
|
7128
7022
|
const items = JSON.parse(fileContent);
|
|
7129
7023
|
if (!Array.isArray(items)) {
|
|
7130
7024
|
console.error("Error: File must contain a JSON array of items");
|
|
@@ -7151,8 +7045,8 @@ async function handleUpsertMany(modelHandle, flags) {
|
|
|
7151
7045
|
console.error("Usage: skedyul instances upsert-many <model> --file data.json --match-field <field> --workplace <subdomain>");
|
|
7152
7046
|
process.exit(1);
|
|
7153
7047
|
}
|
|
7154
|
-
const
|
|
7155
|
-
const fileContent =
|
|
7048
|
+
const fs16 = await import("fs");
|
|
7049
|
+
const fileContent = fs16.readFileSync(filePath, "utf-8");
|
|
7156
7050
|
const items = JSON.parse(fileContent);
|
|
7157
7051
|
if (!Array.isArray(items)) {
|
|
7158
7052
|
console.error("Error: File must contain a JSON array of items");
|
|
@@ -7164,8 +7058,8 @@ async function handleUpsertMany(modelHandle, flags) {
|
|
|
7164
7058
|
|
|
7165
7059
|
// src/cli/commands/build.ts
|
|
7166
7060
|
var import_child_process = require("child_process");
|
|
7167
|
-
var
|
|
7168
|
-
var
|
|
7061
|
+
var fs14 = __toESM(require("fs"));
|
|
7062
|
+
var path15 = __toESM(require("path"));
|
|
7169
7063
|
function printBuildHelp() {
|
|
7170
7064
|
console.log(`
|
|
7171
7065
|
SKEDYUL BUILD - Build your integration
|
|
@@ -7234,8 +7128,8 @@ async function buildCommand(args2) {
|
|
|
7234
7128
|
const cwd = process.cwd();
|
|
7235
7129
|
let configPath = null;
|
|
7236
7130
|
for (const name of CONFIG_FILE_NAMES) {
|
|
7237
|
-
const testPath =
|
|
7238
|
-
if (
|
|
7131
|
+
const testPath = path15.join(cwd, name);
|
|
7132
|
+
if (fs14.existsSync(testPath)) {
|
|
7239
7133
|
configPath = testPath;
|
|
7240
7134
|
break;
|
|
7241
7135
|
}
|
|
@@ -7245,8 +7139,8 @@ async function buildCommand(args2) {
|
|
|
7245
7139
|
console.error("Make sure you are in the root of your integration project.");
|
|
7246
7140
|
process.exit(1);
|
|
7247
7141
|
}
|
|
7248
|
-
console.log(`Loading config from ${
|
|
7249
|
-
const tempConfigPath =
|
|
7142
|
+
console.log(`Loading config from ${path15.basename(configPath)}...`);
|
|
7143
|
+
const tempConfigPath = path15.join(cwd, ".skedyul-tsup.config.mjs");
|
|
7250
7144
|
let createdTempConfig = false;
|
|
7251
7145
|
try {
|
|
7252
7146
|
const config = await loadConfig(configPath);
|
|
@@ -7255,8 +7149,8 @@ async function buildCommand(args2) {
|
|
|
7255
7149
|
const baseExternals = ["skedyul", `skedyul/${computeLayer}`, "zod"];
|
|
7256
7150
|
const userExternals = config.build && "external" in config.build ? config.build.external ?? [] : [];
|
|
7257
7151
|
const allExternals = [...baseExternals, ...userExternals];
|
|
7258
|
-
const userTsupConfig =
|
|
7259
|
-
const hasUserConfig =
|
|
7152
|
+
const userTsupConfig = path15.join(cwd, "tsup.config.ts");
|
|
7153
|
+
const hasUserConfig = fs14.existsSync(userTsupConfig);
|
|
7260
7154
|
console.log(``);
|
|
7261
7155
|
console.log(`Building ${config.name ?? "integration"}...`);
|
|
7262
7156
|
console.log(` Compute layer: ${computeLayer}`);
|
|
@@ -7276,7 +7170,7 @@ async function buildCommand(args2) {
|
|
|
7276
7170
|
}
|
|
7277
7171
|
} else {
|
|
7278
7172
|
const tsupConfigContent = generateTsupConfig(format, allExternals);
|
|
7279
|
-
|
|
7173
|
+
fs14.writeFileSync(tempConfigPath, tsupConfigContent, "utf-8");
|
|
7280
7174
|
createdTempConfig = true;
|
|
7281
7175
|
tsupArgs = [
|
|
7282
7176
|
"tsup",
|
|
@@ -7294,14 +7188,14 @@ async function buildCommand(args2) {
|
|
|
7294
7188
|
});
|
|
7295
7189
|
tsup.on("error", (error) => {
|
|
7296
7190
|
console.error("Failed to start tsup:", error.message);
|
|
7297
|
-
if (createdTempConfig &&
|
|
7298
|
-
|
|
7191
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7192
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7299
7193
|
}
|
|
7300
7194
|
process.exit(1);
|
|
7301
7195
|
});
|
|
7302
7196
|
tsup.on("close", (code) => {
|
|
7303
|
-
if (createdTempConfig &&
|
|
7304
|
-
|
|
7197
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7198
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7305
7199
|
}
|
|
7306
7200
|
if (code === 0) {
|
|
7307
7201
|
console.log(``);
|
|
@@ -7310,8 +7204,8 @@ async function buildCommand(args2) {
|
|
|
7310
7204
|
process.exit(code ?? 0);
|
|
7311
7205
|
});
|
|
7312
7206
|
} catch (error) {
|
|
7313
|
-
if (createdTempConfig &&
|
|
7314
|
-
|
|
7207
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7208
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7315
7209
|
}
|
|
7316
7210
|
console.error(
|
|
7317
7211
|
"Error loading config:",
|
|
@@ -7324,8 +7218,8 @@ async function buildCommand(args2) {
|
|
|
7324
7218
|
// src/cli/commands/smoke-test.ts
|
|
7325
7219
|
var import_child_process2 = require("child_process");
|
|
7326
7220
|
var http3 = __toESM(require("http"));
|
|
7327
|
-
var
|
|
7328
|
-
var
|
|
7221
|
+
var fs15 = __toESM(require("fs"));
|
|
7222
|
+
var path16 = __toESM(require("path"));
|
|
7329
7223
|
var SMOKE_TEST_PORT = 3456;
|
|
7330
7224
|
var HEALTH_CHECK_INTERVAL_MS = 500;
|
|
7331
7225
|
var HEALTH_CHECK_MAX_RETRIES = 30;
|
|
@@ -7356,13 +7250,13 @@ WHAT IT DOES
|
|
|
7356
7250
|
6. Exits with code 0 (success) or 1 (failure)
|
|
7357
7251
|
`);
|
|
7358
7252
|
}
|
|
7359
|
-
function makeRequest(port,
|
|
7253
|
+
function makeRequest(port, path17, method, body) {
|
|
7360
7254
|
return new Promise((resolve10, reject) => {
|
|
7361
7255
|
const postData = body ? JSON.stringify(body) : void 0;
|
|
7362
7256
|
const options = {
|
|
7363
7257
|
hostname: "localhost",
|
|
7364
7258
|
port,
|
|
7365
|
-
path:
|
|
7259
|
+
path: path17,
|
|
7366
7260
|
method,
|
|
7367
7261
|
headers: {
|
|
7368
7262
|
"Content-Type": "application/json",
|
|
@@ -7450,8 +7344,8 @@ async function smokeTestCommand(args2) {
|
|
|
7450
7344
|
const cwd = process.cwd();
|
|
7451
7345
|
let configPath = null;
|
|
7452
7346
|
for (const name of CONFIG_FILE_NAMES) {
|
|
7453
|
-
const testPath =
|
|
7454
|
-
if (
|
|
7347
|
+
const testPath = path16.join(cwd, name);
|
|
7348
|
+
if (fs15.existsSync(testPath)) {
|
|
7455
7349
|
configPath = testPath;
|
|
7456
7350
|
break;
|
|
7457
7351
|
}
|
|
@@ -7467,10 +7361,10 @@ async function smokeTestCommand(args2) {
|
|
|
7467
7361
|
}
|
|
7468
7362
|
const serverExt = computeLayer === "serverless" ? "mjs" : "js";
|
|
7469
7363
|
let serverPath = `dist/server/mcp_server.${serverExt}`;
|
|
7470
|
-
if (!
|
|
7364
|
+
if (!fs15.existsSync(serverPath)) {
|
|
7471
7365
|
const fallbackExt = serverExt === "mjs" ? "js" : "mjs";
|
|
7472
7366
|
const fallbackPath = `dist/server/mcp_server.${fallbackExt}`;
|
|
7473
|
-
if (
|
|
7367
|
+
if (fs15.existsSync(fallbackPath)) {
|
|
7474
7368
|
console.warn(`[SmokeTest] Warning: Expected ${serverPath} but found ${fallbackPath}`);
|
|
7475
7369
|
console.warn(`[SmokeTest] Using ${fallbackPath} instead`);
|
|
7476
7370
|
serverPath = fallbackPath;
|