skedyul 1.2.33 → 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 +785 -902
- package/dist/dedicated/server.js +737 -854
- package/dist/esm/index.mjs +742 -859
- package/dist/index.js +742 -859
- 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 +737 -854
- package/dist/serverless/server.mjs +737 -854
- 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;
|
|
@@ -3324,338 +3413,528 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
|
|
|
3324
3413
|
process.env = originalEnv;
|
|
3325
3414
|
}
|
|
3326
3415
|
return {
|
|
3327
|
-
status: webhookResponse.status ?? 200,
|
|
3328
|
-
body: webhookResponse.body,
|
|
3329
|
-
headers: webhookResponse.headers
|
|
3416
|
+
status: webhookResponse.status ?? 200,
|
|
3417
|
+
body: webhookResponse.body,
|
|
3418
|
+
headers: webhookResponse.headers
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
function isMethodAllowed(webhookRegistry, handle, method) {
|
|
3422
|
+
const webhookDef = webhookRegistry[handle];
|
|
3423
|
+
if (!webhookDef) return false;
|
|
3424
|
+
const allowedMethods = webhookDef.methods ?? ["POST"];
|
|
3425
|
+
return allowedMethods.includes(method);
|
|
3426
|
+
}
|
|
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
|
+
}
|
|
3330
3822
|
};
|
|
3331
3823
|
}
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
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
|
+
}
|
|
3337
3869
|
}
|
|
3338
3870
|
|
|
3339
3871
|
// src/server/dedicated.ts
|
|
3340
|
-
|
|
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
|
-
console.log(`[/config] Checking for config file at: ${CONFIG_FILE_PATH}`);
|
|
3363
|
-
if (fs7.existsSync(CONFIG_FILE_PATH)) {
|
|
3364
|
-
const fileConfig = JSON.parse(fs7.readFileSync(CONFIG_FILE_PATH, "utf-8"));
|
|
3365
|
-
console.log(`[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`);
|
|
3366
|
-
sendJSON(res, 200, fileConfig);
|
|
3367
|
-
return;
|
|
3368
|
-
}
|
|
3369
|
-
console.log("[/config] Config file not found, falling back to runtime serialization");
|
|
3370
|
-
} catch (err) {
|
|
3371
|
-
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3372
|
-
}
|
|
3373
|
-
const serialized = serializeConfig(config);
|
|
3374
|
-
console.log(`[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`);
|
|
3375
|
-
sendJSON(res, 200, serialized);
|
|
3376
|
-
return;
|
|
3377
|
-
}
|
|
3378
|
-
if (pathname.startsWith("/webhooks/") && webhookRegistry) {
|
|
3379
|
-
const handle = pathname.slice("/webhooks/".length);
|
|
3380
|
-
if (!webhookRegistry[handle]) {
|
|
3381
|
-
sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
|
|
3382
|
-
return;
|
|
3383
|
-
}
|
|
3384
|
-
if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
|
|
3385
|
-
sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
|
|
3386
|
-
return;
|
|
3387
|
-
}
|
|
3388
|
-
let rawBody;
|
|
3389
|
-
try {
|
|
3390
|
-
rawBody = await readRawRequestBody(req);
|
|
3391
|
-
} catch {
|
|
3392
|
-
sendJSON(res, 400, { error: "Failed to read request body" });
|
|
3393
|
-
return;
|
|
3394
|
-
}
|
|
3395
|
-
let parsedBody;
|
|
3396
|
-
const contentType = req.headers["content-type"] ?? "";
|
|
3397
|
-
if (contentType.includes("application/json")) {
|
|
3398
|
-
try {
|
|
3399
|
-
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3400
|
-
} catch {
|
|
3401
|
-
parsedBody = rawBody;
|
|
3402
|
-
}
|
|
3403
|
-
} else {
|
|
3404
|
-
parsedBody = rawBody;
|
|
3405
|
-
}
|
|
3406
|
-
const parseResult = parseWebhookRequest(
|
|
3407
|
-
parsedBody,
|
|
3408
|
-
req.method ?? "POST",
|
|
3409
|
-
url.toString(),
|
|
3410
|
-
pathname,
|
|
3411
|
-
req.headers,
|
|
3412
|
-
Object.fromEntries(url.searchParams.entries()),
|
|
3413
|
-
rawBody,
|
|
3414
|
-
req.headers["x-skedyul-app-id"],
|
|
3415
|
-
req.headers["x-skedyul-app-version-id"]
|
|
3416
|
-
);
|
|
3417
|
-
if ("error" in parseResult) {
|
|
3418
|
-
sendJSON(res, 400, { error: parseResult.error });
|
|
3419
|
-
return;
|
|
3420
|
-
}
|
|
3421
|
-
const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
|
|
3422
|
-
const responseHeaders = {
|
|
3423
|
-
...result.headers
|
|
3424
|
-
};
|
|
3425
|
-
if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
|
|
3426
|
-
responseHeaders["Content-Type"] = "application/json";
|
|
3427
|
-
}
|
|
3428
|
-
res.writeHead(result.status, responseHeaders);
|
|
3429
|
-
if (result.body !== void 0) {
|
|
3430
|
-
if (typeof result.body === "string") {
|
|
3431
|
-
res.end(result.body);
|
|
3432
|
-
} else {
|
|
3433
|
-
res.end(JSON.stringify(result.body));
|
|
3434
|
-
}
|
|
3435
|
-
} else {
|
|
3436
|
-
res.end();
|
|
3437
|
-
}
|
|
3438
|
-
return;
|
|
3439
|
-
}
|
|
3440
|
-
if (pathname === "/estimate" && req.method === "POST") {
|
|
3441
|
-
let estimateBody;
|
|
3442
|
-
try {
|
|
3443
|
-
estimateBody = await parseJSONBody(req);
|
|
3444
|
-
} catch {
|
|
3445
|
-
sendJSON(res, 400, {
|
|
3446
|
-
error: {
|
|
3447
|
-
code: -32700,
|
|
3448
|
-
message: "Parse error"
|
|
3449
|
-
}
|
|
3450
|
-
});
|
|
3451
|
-
return;
|
|
3452
|
-
}
|
|
3453
|
-
try {
|
|
3454
|
-
const estimateResponse = await callTool(estimateBody.name, {
|
|
3455
|
-
inputs: estimateBody.inputs,
|
|
3456
|
-
estimate: true
|
|
3457
|
-
});
|
|
3458
|
-
sendJSON(res, 200, {
|
|
3459
|
-
billing: estimateResponse.billing ?? { credits: 0 }
|
|
3460
|
-
});
|
|
3461
|
-
} catch (err) {
|
|
3462
|
-
sendJSON(res, 500, {
|
|
3463
|
-
error: {
|
|
3464
|
-
code: -32603,
|
|
3465
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3466
|
-
}
|
|
3467
|
-
});
|
|
3468
|
-
}
|
|
3469
|
-
return;
|
|
3470
|
-
}
|
|
3471
|
-
if (pathname === "/oauth_callback" && req.method === "POST") {
|
|
3472
|
-
let parsedBody;
|
|
3473
|
-
try {
|
|
3474
|
-
parsedBody = await parseJSONBody(req);
|
|
3475
|
-
} catch (err) {
|
|
3476
|
-
console.error("[OAuth Callback] Failed to parse JSON body:", err);
|
|
3477
|
-
sendJSON(res, 400, {
|
|
3478
|
-
error: { code: -32700, message: "Parse error" }
|
|
3479
|
-
});
|
|
3480
|
-
return;
|
|
3481
|
-
}
|
|
3482
|
-
const result = await handleOAuthCallback(parsedBody, config.hooks);
|
|
3483
|
-
sendJSON(res, result.status, result.body);
|
|
3484
|
-
return;
|
|
3485
|
-
}
|
|
3486
|
-
if (pathname === "/install" && req.method === "POST") {
|
|
3487
|
-
let installBody;
|
|
3488
|
-
try {
|
|
3489
|
-
installBody = await parseJSONBody(req);
|
|
3490
|
-
} catch {
|
|
3491
|
-
sendJSON(res, 400, {
|
|
3492
|
-
error: { code: -32700, message: "Parse error" }
|
|
3493
|
-
});
|
|
3494
|
-
return;
|
|
3495
|
-
}
|
|
3496
|
-
const result = await handleInstall(installBody, config.hooks);
|
|
3497
|
-
sendJSON(res, result.status, result.body);
|
|
3498
|
-
return;
|
|
3499
|
-
}
|
|
3500
|
-
if (pathname === "/uninstall" && req.method === "POST") {
|
|
3501
|
-
let uninstallBody;
|
|
3502
|
-
try {
|
|
3503
|
-
uninstallBody = await parseJSONBody(req);
|
|
3504
|
-
} catch {
|
|
3505
|
-
sendJSON(res, 400, {
|
|
3506
|
-
error: { code: -32700, message: "Parse error" }
|
|
3507
|
-
});
|
|
3508
|
-
return;
|
|
3509
|
-
}
|
|
3510
|
-
const result = await handleUninstall(uninstallBody, config.hooks);
|
|
3511
|
-
sendJSON(res, result.status, result.body);
|
|
3512
|
-
return;
|
|
3513
|
-
}
|
|
3514
|
-
if (pathname === "/provision" && req.method === "POST") {
|
|
3515
|
-
let provisionBody;
|
|
3516
|
-
try {
|
|
3517
|
-
provisionBody = await parseJSONBody(req);
|
|
3518
|
-
} catch {
|
|
3519
|
-
sendJSON(res, 400, {
|
|
3520
|
-
error: { code: -32700, message: "Parse error" }
|
|
3521
|
-
});
|
|
3522
|
-
return;
|
|
3523
|
-
}
|
|
3524
|
-
const result = await handleProvision(provisionBody, config.hooks);
|
|
3525
|
-
sendJSON(res, result.status, result.body);
|
|
3526
|
-
return;
|
|
3527
|
-
}
|
|
3528
|
-
if (pathname === "/core" && req.method === "POST") {
|
|
3529
|
-
let coreBody;
|
|
3530
|
-
try {
|
|
3531
|
-
coreBody = await parseJSONBody(req);
|
|
3532
|
-
} catch {
|
|
3533
|
-
sendJSON(res, 400, {
|
|
3534
|
-
error: {
|
|
3535
|
-
code: -32700,
|
|
3536
|
-
message: "Parse error"
|
|
3537
|
-
}
|
|
3538
|
-
});
|
|
3539
|
-
return;
|
|
3540
|
-
}
|
|
3541
|
-
if (!coreBody?.method) {
|
|
3542
|
-
sendJSON(res, 400, {
|
|
3543
|
-
error: {
|
|
3544
|
-
code: -32602,
|
|
3545
|
-
message: "Missing method"
|
|
3546
|
-
}
|
|
3547
|
-
});
|
|
3548
|
-
return;
|
|
3549
|
-
}
|
|
3550
|
-
const method = coreBody.method;
|
|
3551
|
-
const result = await handleCoreMethod(method, coreBody.params);
|
|
3552
|
-
sendCoreResult(result);
|
|
3553
|
-
return;
|
|
3554
|
-
}
|
|
3555
|
-
if (pathname === "/core/webhook" && req.method === "POST") {
|
|
3556
|
-
let rawWebhookBody;
|
|
3557
|
-
try {
|
|
3558
|
-
rawWebhookBody = await readRawRequestBody(req);
|
|
3559
|
-
} catch {
|
|
3560
|
-
sendJSON(res, 400, { status: "parse-error" });
|
|
3561
|
-
return;
|
|
3562
|
-
}
|
|
3563
|
-
let webhookBody;
|
|
3564
|
-
try {
|
|
3565
|
-
webhookBody = rawWebhookBody ? JSON.parse(rawWebhookBody) : {};
|
|
3566
|
-
} catch {
|
|
3567
|
-
sendJSON(res, 400, { status: "parse-error" });
|
|
3568
|
-
return;
|
|
3569
|
-
}
|
|
3570
|
-
const normalizedHeaders = Object.fromEntries(
|
|
3571
|
-
Object.entries(req.headers).map(([key, value]) => [
|
|
3572
|
-
key,
|
|
3573
|
-
typeof value === "string" ? value : value?.[0] ?? ""
|
|
3574
|
-
])
|
|
3575
|
-
);
|
|
3576
|
-
const coreWebhookRequest = {
|
|
3577
|
-
method: req.method ?? "POST",
|
|
3578
|
-
headers: normalizedHeaders,
|
|
3579
|
-
body: webhookBody,
|
|
3580
|
-
query: Object.fromEntries(url.searchParams.entries()),
|
|
3581
|
-
url: url.toString(),
|
|
3582
|
-
path: url.pathname,
|
|
3583
|
-
rawBody: rawWebhookBody ? Buffer.from(rawWebhookBody, "utf-8") : void 0
|
|
3584
|
-
};
|
|
3585
|
-
const webhookResponse = await coreApiService.dispatchWebhook(
|
|
3586
|
-
coreWebhookRequest
|
|
3587
|
-
);
|
|
3588
|
-
res.writeHead(webhookResponse.status, {
|
|
3589
|
-
"Content-Type": "application/json"
|
|
3590
|
-
});
|
|
3591
|
-
res.end(JSON.stringify(webhookResponse.body ?? {}));
|
|
3924
|
+
if (pathname === "/mcp" && req.method === "POST") {
|
|
3925
|
+
await handleMcpWithSdkTransport(req, res, url, ctx, mcpServer);
|
|
3592
3926
|
return;
|
|
3593
3927
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
method: body.method,
|
|
3600
|
-
toolName: body.params?.name,
|
|
3601
|
-
hasArguments: !!body.params?.arguments,
|
|
3602
|
-
argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
|
|
3603
|
-
hasEnv: !!body.params?.arguments?.env,
|
|
3604
|
-
envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
|
|
3605
|
-
hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
|
|
3606
|
-
}, null, 2));
|
|
3607
|
-
}
|
|
3608
|
-
if (body?.method === "tools/list") {
|
|
3609
|
-
sendJSON(res, 200, {
|
|
3610
|
-
jsonrpc: "2.0",
|
|
3611
|
-
id: body.id ?? null,
|
|
3612
|
-
result: { tools }
|
|
3613
|
-
});
|
|
3614
|
-
return;
|
|
3615
|
-
}
|
|
3616
|
-
if (body?.method === "webhooks/list") {
|
|
3617
|
-
const webhooks = webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
3618
|
-
name: w.name,
|
|
3619
|
-
description: w.description,
|
|
3620
|
-
methods: w.methods ?? ["POST"],
|
|
3621
|
-
type: w.type ?? "WEBHOOK"
|
|
3622
|
-
})) : [];
|
|
3623
|
-
sendJSON(res, 200, {
|
|
3624
|
-
jsonrpc: "2.0",
|
|
3625
|
-
id: body.id ?? null,
|
|
3626
|
-
result: { webhooks }
|
|
3627
|
-
});
|
|
3628
|
-
return;
|
|
3629
|
-
}
|
|
3630
|
-
const transport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
3631
|
-
sessionIdGenerator: void 0,
|
|
3632
|
-
enableJsonResponse: true
|
|
3633
|
-
});
|
|
3634
|
-
res.on("close", () => {
|
|
3635
|
-
transport.close();
|
|
3636
|
-
});
|
|
3637
|
-
await mcpServer.connect(transport);
|
|
3638
|
-
await transport.handleRequest(req, res, body);
|
|
3639
|
-
} catch (err) {
|
|
3640
|
-
sendJSON(res, 500, {
|
|
3641
|
-
jsonrpc: "2.0",
|
|
3642
|
-
id: null,
|
|
3643
|
-
error: {
|
|
3644
|
-
code: -32603,
|
|
3645
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3646
|
-
}
|
|
3647
|
-
});
|
|
3648
|
-
}
|
|
3928
|
+
let rawBody;
|
|
3929
|
+
try {
|
|
3930
|
+
rawBody = await readRawRequestBody(req);
|
|
3931
|
+
} catch {
|
|
3932
|
+
sendJSON(res, 400, { error: "Failed to read request body" });
|
|
3649
3933
|
return;
|
|
3650
3934
|
}
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
error: {
|
|
3655
|
-
code: -32601,
|
|
3656
|
-
message: "Not Found"
|
|
3657
|
-
}
|
|
3658
|
-
});
|
|
3935
|
+
const unifiedReq = fromHttpRequest(req, url, rawBody);
|
|
3936
|
+
const response = await routeRequest(unifiedReq, ctx);
|
|
3937
|
+
sendUnifiedResponse(res, response, defaultHeaders);
|
|
3659
3938
|
} catch (err) {
|
|
3660
3939
|
sendJSON(res, 500, {
|
|
3661
3940
|
jsonrpc: "2.0",
|
|
@@ -3682,491 +3961,95 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
3682
3961
|
getHealthStatus: () => state.getHealthStatus()
|
|
3683
3962
|
};
|
|
3684
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
|
+
}
|
|
3685
4027
|
|
|
3686
4028
|
// src/server/serverless.ts
|
|
3687
|
-
var fs8 = __toESM(require("fs"));
|
|
3688
|
-
var path7 = __toESM(require("path"));
|
|
3689
|
-
var CONFIG_FILE_PATH2 = process.env.LAMBDA_TASK_ROOT ? path7.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
3690
4029
|
function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
3691
|
-
const
|
|
4030
|
+
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
3692
4031
|
const registry = config.tools;
|
|
3693
4032
|
const webhookRegistry = config.webhooks;
|
|
3694
4033
|
let hasLoggedStartup = false;
|
|
4034
|
+
const ctx = {
|
|
4035
|
+
config,
|
|
4036
|
+
tools,
|
|
4037
|
+
registry,
|
|
4038
|
+
webhookRegistry,
|
|
4039
|
+
callTool,
|
|
4040
|
+
state,
|
|
4041
|
+
mcpServer,
|
|
4042
|
+
defaultHeaders
|
|
4043
|
+
};
|
|
3695
4044
|
return {
|
|
3696
4045
|
async handler(event) {
|
|
3697
4046
|
if (!hasLoggedStartup) {
|
|
3698
4047
|
printStartupLog(config, tools);
|
|
3699
4048
|
hasLoggedStartup = true;
|
|
3700
4049
|
}
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
if (method === "OPTIONS") {
|
|
3705
|
-
return createResponse(200, { message: "OK" }, headers);
|
|
3706
|
-
}
|
|
3707
|
-
if (path17.startsWith("/webhooks/") && webhookRegistry) {
|
|
3708
|
-
const handle = path17.slice("/webhooks/".length);
|
|
3709
|
-
if (!webhookRegistry[handle]) {
|
|
3710
|
-
return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
|
|
3711
|
-
}
|
|
3712
|
-
if (!isMethodAllowed(webhookRegistry, handle, method)) {
|
|
3713
|
-
return createResponse(405, { error: `Method ${method} not allowed` }, headers);
|
|
3714
|
-
}
|
|
3715
|
-
const rawBody = event.body ?? "";
|
|
3716
|
-
let parsedBody;
|
|
3717
|
-
const contentType = event.headers?.["content-type"] ?? event.headers?.["Content-Type"] ?? "";
|
|
3718
|
-
if (contentType.includes("application/json")) {
|
|
3719
|
-
try {
|
|
3720
|
-
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3721
|
-
} catch {
|
|
3722
|
-
parsedBody = rawBody;
|
|
3723
|
-
}
|
|
3724
|
-
} else {
|
|
3725
|
-
parsedBody = rawBody;
|
|
3726
|
-
}
|
|
3727
|
-
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
3728
|
-
const protocol = forwardedProto ?? "https";
|
|
3729
|
-
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3730
|
-
const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
|
|
3731
|
-
const webhookUrl = `${protocol}://${host}${path17}${queryString}`;
|
|
3732
|
-
const parseResult = parseWebhookRequest(
|
|
3733
|
-
parsedBody,
|
|
3734
|
-
method,
|
|
3735
|
-
webhookUrl,
|
|
3736
|
-
path17,
|
|
3737
|
-
event.headers,
|
|
3738
|
-
event.queryStringParameters ?? {},
|
|
3739
|
-
rawBody,
|
|
3740
|
-
event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
|
|
3741
|
-
event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
|
|
3742
|
-
);
|
|
3743
|
-
if ("error" in parseResult) {
|
|
3744
|
-
return createResponse(400, { error: parseResult.error }, headers);
|
|
3745
|
-
}
|
|
3746
|
-
const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
|
|
3747
|
-
const responseHeaders = {
|
|
3748
|
-
...headers,
|
|
3749
|
-
...result.headers
|
|
3750
|
-
};
|
|
3751
|
-
return {
|
|
3752
|
-
statusCode: result.status,
|
|
3753
|
-
headers: responseHeaders,
|
|
3754
|
-
body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
|
|
3755
|
-
};
|
|
3756
|
-
}
|
|
3757
|
-
if (path17 === "/core" && method === "POST") {
|
|
3758
|
-
let coreBody;
|
|
3759
|
-
try {
|
|
3760
|
-
coreBody = event.body ? JSON.parse(event.body) : {};
|
|
3761
|
-
} catch {
|
|
3762
|
-
return createResponse(
|
|
3763
|
-
400,
|
|
3764
|
-
{
|
|
3765
|
-
error: {
|
|
3766
|
-
code: -32700,
|
|
3767
|
-
message: "Parse error"
|
|
3768
|
-
}
|
|
3769
|
-
},
|
|
3770
|
-
headers
|
|
3771
|
-
);
|
|
3772
|
-
}
|
|
3773
|
-
if (!coreBody?.method) {
|
|
3774
|
-
return createResponse(
|
|
3775
|
-
400,
|
|
3776
|
-
{
|
|
3777
|
-
error: {
|
|
3778
|
-
code: -32602,
|
|
3779
|
-
message: "Missing method"
|
|
3780
|
-
}
|
|
3781
|
-
},
|
|
3782
|
-
headers
|
|
3783
|
-
);
|
|
3784
|
-
}
|
|
3785
|
-
const coreMethod = coreBody.method;
|
|
3786
|
-
const result = await handleCoreMethod(coreMethod, coreBody.params);
|
|
3787
|
-
return createResponse(result.status, result.payload, headers);
|
|
3788
|
-
}
|
|
3789
|
-
if (path17 === "/core/webhook" && method === "POST") {
|
|
3790
|
-
const rawWebhookBody = event.body ?? "";
|
|
3791
|
-
let webhookBody;
|
|
3792
|
-
try {
|
|
3793
|
-
webhookBody = rawWebhookBody ? JSON.parse(rawWebhookBody) : {};
|
|
3794
|
-
} catch {
|
|
3795
|
-
return createResponse(
|
|
3796
|
-
400,
|
|
3797
|
-
{ status: "parse-error" },
|
|
3798
|
-
headers
|
|
3799
|
-
);
|
|
3800
|
-
}
|
|
3801
|
-
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
3802
|
-
const protocol = forwardedProto ?? "https";
|
|
3803
|
-
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3804
|
-
const webhookUrl = `${protocol}://${host}${path17}`;
|
|
3805
|
-
const coreWebhookRequest = {
|
|
3806
|
-
method,
|
|
3807
|
-
headers: event.headers ?? {},
|
|
3808
|
-
body: webhookBody,
|
|
3809
|
-
query: event.queryStringParameters ?? {},
|
|
3810
|
-
url: webhookUrl,
|
|
3811
|
-
path: path17,
|
|
3812
|
-
rawBody: rawWebhookBody ? Buffer.from(rawWebhookBody, "utf-8") : void 0
|
|
3813
|
-
};
|
|
3814
|
-
const webhookResponse = await coreApiService.dispatchWebhook(
|
|
3815
|
-
coreWebhookRequest
|
|
3816
|
-
);
|
|
3817
|
-
return createResponse(
|
|
3818
|
-
webhookResponse.status,
|
|
3819
|
-
webhookResponse.body ?? {},
|
|
3820
|
-
headers
|
|
3821
|
-
);
|
|
3822
|
-
}
|
|
3823
|
-
if (path17 === "/estimate" && method === "POST") {
|
|
3824
|
-
let estimateBody;
|
|
3825
|
-
try {
|
|
3826
|
-
estimateBody = event.body ? JSON.parse(event.body) : {};
|
|
3827
|
-
} catch {
|
|
3828
|
-
return createResponse(
|
|
3829
|
-
400,
|
|
3830
|
-
{
|
|
3831
|
-
error: {
|
|
3832
|
-
code: -32700,
|
|
3833
|
-
message: "Parse error"
|
|
3834
|
-
}
|
|
3835
|
-
},
|
|
3836
|
-
headers
|
|
3837
|
-
);
|
|
3838
|
-
}
|
|
3839
|
-
try {
|
|
3840
|
-
const toolName = estimateBody.name;
|
|
3841
|
-
const toolArgs = estimateBody.inputs ?? {};
|
|
3842
|
-
let toolKey = null;
|
|
3843
|
-
let tool = null;
|
|
3844
|
-
for (const [key, t] of Object.entries(registry)) {
|
|
3845
|
-
if (t.name === toolName || key === toolName) {
|
|
3846
|
-
toolKey = key;
|
|
3847
|
-
tool = t;
|
|
3848
|
-
break;
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
if (!tool || !toolKey) {
|
|
3852
|
-
return createResponse(
|
|
3853
|
-
400,
|
|
3854
|
-
{
|
|
3855
|
-
error: {
|
|
3856
|
-
code: -32602,
|
|
3857
|
-
message: `Tool "${toolName}" not found`
|
|
3858
|
-
}
|
|
3859
|
-
},
|
|
3860
|
-
headers
|
|
3861
|
-
);
|
|
3862
|
-
}
|
|
3863
|
-
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
3864
|
-
const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
3865
|
-
const estimateResponse = await callTool(toolKey, {
|
|
3866
|
-
inputs: validatedArgs,
|
|
3867
|
-
estimate: true
|
|
3868
|
-
});
|
|
3869
|
-
return createResponse(
|
|
3870
|
-
200,
|
|
3871
|
-
{
|
|
3872
|
-
billing: estimateResponse.billing ?? { credits: 0 }
|
|
3873
|
-
},
|
|
3874
|
-
headers
|
|
3875
|
-
);
|
|
3876
|
-
} catch (err) {
|
|
3877
|
-
return createResponse(
|
|
3878
|
-
500,
|
|
3879
|
-
{
|
|
3880
|
-
error: {
|
|
3881
|
-
code: -32603,
|
|
3882
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
3883
|
-
}
|
|
3884
|
-
},
|
|
3885
|
-
headers
|
|
3886
|
-
);
|
|
3887
|
-
}
|
|
3888
|
-
}
|
|
3889
|
-
if (path17 === "/install" && method === "POST") {
|
|
3890
|
-
let installBody;
|
|
3891
|
-
try {
|
|
3892
|
-
installBody = event.body ? JSON.parse(event.body) : {};
|
|
3893
|
-
} catch {
|
|
3894
|
-
return createResponse(
|
|
3895
|
-
400,
|
|
3896
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3897
|
-
headers
|
|
3898
|
-
);
|
|
3899
|
-
}
|
|
3900
|
-
const result = await handleInstall(installBody, config.hooks);
|
|
3901
|
-
return createResponse(result.status, result.body, headers);
|
|
3902
|
-
}
|
|
3903
|
-
if (path17 === "/uninstall" && method === "POST") {
|
|
3904
|
-
let uninstallBody;
|
|
3905
|
-
try {
|
|
3906
|
-
uninstallBody = event.body ? JSON.parse(event.body) : {};
|
|
3907
|
-
} catch {
|
|
3908
|
-
return createResponse(
|
|
3909
|
-
400,
|
|
3910
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3911
|
-
headers
|
|
3912
|
-
);
|
|
3913
|
-
}
|
|
3914
|
-
const result = await handleUninstall(uninstallBody, config.hooks);
|
|
3915
|
-
return createResponse(result.status, result.body, headers);
|
|
3916
|
-
}
|
|
3917
|
-
if (path17 === "/provision" && method === "POST") {
|
|
3918
|
-
let provisionBody;
|
|
3919
|
-
try {
|
|
3920
|
-
provisionBody = event.body ? JSON.parse(event.body) : {};
|
|
3921
|
-
} catch {
|
|
3922
|
-
return createResponse(
|
|
3923
|
-
400,
|
|
3924
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3925
|
-
headers
|
|
3926
|
-
);
|
|
3927
|
-
}
|
|
3928
|
-
const result = await handleProvision(provisionBody, config.hooks);
|
|
3929
|
-
return createResponse(result.status, result.body, headers);
|
|
3930
|
-
}
|
|
3931
|
-
if (path17 === "/oauth_callback" && method === "POST") {
|
|
3932
|
-
let parsedBody;
|
|
3933
|
-
try {
|
|
3934
|
-
parsedBody = event.body ? JSON.parse(event.body) : {};
|
|
3935
|
-
} catch (err) {
|
|
3936
|
-
console.error("[OAuth Callback] Failed to parse JSON body:", err);
|
|
3937
|
-
return createResponse(
|
|
3938
|
-
400,
|
|
3939
|
-
{ error: { code: -32700, message: "Parse error" } },
|
|
3940
|
-
headers
|
|
3941
|
-
);
|
|
3942
|
-
}
|
|
3943
|
-
const result = await handleOAuthCallback(parsedBody, config.hooks);
|
|
3944
|
-
return createResponse(result.status, result.body, headers);
|
|
3945
|
-
}
|
|
3946
|
-
if (path17 === "/health" && method === "GET") {
|
|
3947
|
-
return createResponse(200, state.getHealthStatus(), headers);
|
|
3948
|
-
}
|
|
3949
|
-
if (path17 === "/config" && method === "GET") {
|
|
3950
|
-
try {
|
|
3951
|
-
console.log(`[/config] Checking for config file at: ${CONFIG_FILE_PATH2}`);
|
|
3952
|
-
if (fs8.existsSync(CONFIG_FILE_PATH2)) {
|
|
3953
|
-
const fileConfig = JSON.parse(fs8.readFileSync(CONFIG_FILE_PATH2, "utf-8"));
|
|
3954
|
-
console.log(`[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`);
|
|
3955
|
-
return createResponse(200, fileConfig, headers);
|
|
3956
|
-
}
|
|
3957
|
-
console.log("[/config] Config file not found, falling back to runtime serialization");
|
|
3958
|
-
} catch (err) {
|
|
3959
|
-
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3960
|
-
}
|
|
3961
|
-
const serialized = serializeConfig(config);
|
|
3962
|
-
console.log(`[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`);
|
|
3963
|
-
return createResponse(200, serialized, headers);
|
|
3964
|
-
}
|
|
3965
|
-
if (path17 === "/mcp" && method === "POST") {
|
|
3966
|
-
let body;
|
|
3967
|
-
try {
|
|
3968
|
-
body = event.body ? JSON.parse(event.body) : {};
|
|
3969
|
-
} catch {
|
|
3970
|
-
return createResponse(
|
|
3971
|
-
400,
|
|
3972
|
-
{
|
|
3973
|
-
jsonrpc: "2.0",
|
|
3974
|
-
id: null,
|
|
3975
|
-
error: {
|
|
3976
|
-
code: -32700,
|
|
3977
|
-
message: "Parse error"
|
|
3978
|
-
}
|
|
3979
|
-
},
|
|
3980
|
-
headers
|
|
3981
|
-
);
|
|
3982
|
-
}
|
|
3983
|
-
try {
|
|
3984
|
-
const { jsonrpc, id, method: rpcMethod, params } = body;
|
|
3985
|
-
if (jsonrpc !== "2.0") {
|
|
3986
|
-
return createResponse(
|
|
3987
|
-
400,
|
|
3988
|
-
{
|
|
3989
|
-
jsonrpc: "2.0",
|
|
3990
|
-
id,
|
|
3991
|
-
error: {
|
|
3992
|
-
code: -32600,
|
|
3993
|
-
message: "Invalid Request"
|
|
3994
|
-
}
|
|
3995
|
-
},
|
|
3996
|
-
headers
|
|
3997
|
-
);
|
|
3998
|
-
}
|
|
3999
|
-
let result;
|
|
4000
|
-
if (rpcMethod === "tools/list") {
|
|
4001
|
-
result = { tools };
|
|
4002
|
-
} else if (rpcMethod === "tools/call") {
|
|
4003
|
-
const toolName = params?.name;
|
|
4004
|
-
const rawArgs = params?.arguments ?? {};
|
|
4005
|
-
console.log("[serverless.ts /mcp] Received tools/call request:", JSON.stringify({
|
|
4006
|
-
toolName,
|
|
4007
|
-
hasArguments: !!params?.arguments,
|
|
4008
|
-
argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
|
|
4009
|
-
hasEnv: "env" in rawArgs,
|
|
4010
|
-
envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
|
|
4011
|
-
hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
|
|
4012
|
-
}, null, 2));
|
|
4013
|
-
const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
|
|
4014
|
-
const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
|
|
4015
|
-
const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
|
|
4016
|
-
const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
|
|
4017
|
-
const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
|
|
4018
|
-
console.log("[serverless.ts /mcp] Extracted env:", JSON.stringify({
|
|
4019
|
-
hasSkedyulFormat,
|
|
4020
|
-
hasToolEnv: !!toolEnv,
|
|
4021
|
-
toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
4022
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
4023
|
-
}, null, 2));
|
|
4024
|
-
let toolKey = null;
|
|
4025
|
-
let tool = null;
|
|
4026
|
-
for (const [key, t] of Object.entries(registry)) {
|
|
4027
|
-
if (t.name === toolName || key === toolName) {
|
|
4028
|
-
toolKey = key;
|
|
4029
|
-
tool = t;
|
|
4030
|
-
break;
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
4033
|
-
if (!tool || !toolKey) {
|
|
4034
|
-
return createResponse(
|
|
4035
|
-
200,
|
|
4036
|
-
{
|
|
4037
|
-
jsonrpc: "2.0",
|
|
4038
|
-
id,
|
|
4039
|
-
error: {
|
|
4040
|
-
code: -32602,
|
|
4041
|
-
message: `Tool "${toolName}" not found`
|
|
4042
|
-
}
|
|
4043
|
-
},
|
|
4044
|
-
headers
|
|
4045
|
-
);
|
|
4046
|
-
}
|
|
4047
|
-
try {
|
|
4048
|
-
const inputSchema = getZodSchema3(tool.inputSchema);
|
|
4049
|
-
const outputSchema = getZodSchema3(tool.outputSchema);
|
|
4050
|
-
const hasOutputSchema = Boolean(outputSchema);
|
|
4051
|
-
const validatedInputs = inputSchema ? inputSchema.parse(toolInputs) : toolInputs;
|
|
4052
|
-
const toolResult = await callTool(toolKey, {
|
|
4053
|
-
inputs: validatedInputs,
|
|
4054
|
-
context: toolContext,
|
|
4055
|
-
env: toolEnv,
|
|
4056
|
-
invocation: toolInvocation
|
|
4057
|
-
});
|
|
4058
|
-
if (toolResult.error) {
|
|
4059
|
-
const errorOutput = { error: toolResult.error };
|
|
4060
|
-
result = {
|
|
4061
|
-
content: [{ type: "text", text: JSON.stringify(errorOutput) }],
|
|
4062
|
-
// Don't provide structuredContent for error responses when tool has outputSchema
|
|
4063
|
-
// because the error response won't match the success schema and MCP SDK validates it
|
|
4064
|
-
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
4065
|
-
isError: true,
|
|
4066
|
-
billing: toolResult.billing
|
|
4067
|
-
};
|
|
4068
|
-
} else {
|
|
4069
|
-
const outputData = toolResult.output;
|
|
4070
|
-
let structuredContent;
|
|
4071
|
-
if (outputData) {
|
|
4072
|
-
structuredContent = { ...outputData, __effect: toolResult.effect };
|
|
4073
|
-
} else if (toolResult.effect) {
|
|
4074
|
-
structuredContent = { __effect: toolResult.effect };
|
|
4075
|
-
} else if (hasOutputSchema) {
|
|
4076
|
-
structuredContent = {};
|
|
4077
|
-
}
|
|
4078
|
-
result = {
|
|
4079
|
-
content: [{ type: "text", text: JSON.stringify(toolResult.output) }],
|
|
4080
|
-
structuredContent,
|
|
4081
|
-
billing: toolResult.billing
|
|
4082
|
-
};
|
|
4083
|
-
}
|
|
4084
|
-
} catch (validationError) {
|
|
4085
|
-
return createResponse(
|
|
4086
|
-
200,
|
|
4087
|
-
{
|
|
4088
|
-
jsonrpc: "2.0",
|
|
4089
|
-
id,
|
|
4090
|
-
error: {
|
|
4091
|
-
code: -32602,
|
|
4092
|
-
message: validationError instanceof Error ? validationError.message : "Invalid arguments"
|
|
4093
|
-
}
|
|
4094
|
-
},
|
|
4095
|
-
headers
|
|
4096
|
-
);
|
|
4097
|
-
}
|
|
4098
|
-
} else if (rpcMethod === "webhooks/list") {
|
|
4099
|
-
const webhooks = webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
4100
|
-
name: w.name,
|
|
4101
|
-
description: w.description,
|
|
4102
|
-
methods: w.methods ?? ["POST"],
|
|
4103
|
-
type: w.type ?? "WEBHOOK"
|
|
4104
|
-
})) : [];
|
|
4105
|
-
result = { webhooks };
|
|
4106
|
-
} else {
|
|
4107
|
-
return createResponse(
|
|
4108
|
-
200,
|
|
4109
|
-
{
|
|
4110
|
-
jsonrpc: "2.0",
|
|
4111
|
-
id,
|
|
4112
|
-
error: {
|
|
4113
|
-
code: -32601,
|
|
4114
|
-
message: `Method not found: ${rpcMethod}`
|
|
4115
|
-
}
|
|
4116
|
-
},
|
|
4117
|
-
headers
|
|
4118
|
-
);
|
|
4119
|
-
}
|
|
4120
|
-
return createResponse(
|
|
4121
|
-
200,
|
|
4122
|
-
{
|
|
4123
|
-
jsonrpc: "2.0",
|
|
4124
|
-
id,
|
|
4125
|
-
result
|
|
4126
|
-
},
|
|
4127
|
-
headers
|
|
4128
|
-
);
|
|
4129
|
-
} catch (err) {
|
|
4130
|
-
return createResponse(
|
|
4131
|
-
500,
|
|
4132
|
-
{
|
|
4133
|
-
jsonrpc: "2.0",
|
|
4134
|
-
id: body?.id ?? null,
|
|
4135
|
-
error: {
|
|
4136
|
-
code: -32603,
|
|
4137
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
4138
|
-
}
|
|
4139
|
-
},
|
|
4140
|
-
headers
|
|
4141
|
-
);
|
|
4142
|
-
}
|
|
4143
|
-
}
|
|
4144
|
-
return createResponse(
|
|
4145
|
-
404,
|
|
4146
|
-
{
|
|
4147
|
-
jsonrpc: "2.0",
|
|
4148
|
-
id: null,
|
|
4149
|
-
error: {
|
|
4150
|
-
code: -32601,
|
|
4151
|
-
message: "Not Found"
|
|
4152
|
-
}
|
|
4153
|
-
},
|
|
4154
|
-
headers
|
|
4155
|
-
);
|
|
4156
|
-
} catch (err) {
|
|
4157
|
-
return createResponse(
|
|
4158
|
-
500,
|
|
4159
|
-
{
|
|
4160
|
-
jsonrpc: "2.0",
|
|
4161
|
-
id: null,
|
|
4162
|
-
error: {
|
|
4163
|
-
code: -32603,
|
|
4164
|
-
message: err instanceof Error ? err.message : String(err ?? "")
|
|
4165
|
-
}
|
|
4166
|
-
},
|
|
4167
|
-
headers
|
|
4168
|
-
);
|
|
4169
|
-
}
|
|
4050
|
+
const req = fromLambdaEvent(event);
|
|
4051
|
+
const response = await routeRequest(req, ctx);
|
|
4052
|
+
return toLambdaResponse(response, defaultHeaders);
|
|
4170
4053
|
},
|
|
4171
4054
|
getHealthStatus: () => state.getHealthStatus()
|
|
4172
4055
|
};
|
|
@@ -4994,12 +4877,12 @@ Press Ctrl+C to stop`);
|
|
|
4994
4877
|
}
|
|
4995
4878
|
|
|
4996
4879
|
// src/cli/commands/validate.ts
|
|
4997
|
-
var
|
|
4880
|
+
var fs9 = __toESM(require("fs"));
|
|
4998
4881
|
var path10 = __toESM(require("path"));
|
|
4999
4882
|
init_utils();
|
|
5000
4883
|
|
|
5001
4884
|
// src/config/loader.ts
|
|
5002
|
-
var
|
|
4885
|
+
var fs8 = __toESM(require("fs"));
|
|
5003
4886
|
var path8 = __toESM(require("path"));
|
|
5004
4887
|
var os2 = __toESM(require("os"));
|
|
5005
4888
|
var CONFIG_FILE_NAMES = [
|
|
@@ -5009,7 +4892,7 @@ var CONFIG_FILE_NAMES = [
|
|
|
5009
4892
|
"skedyul.config.cjs"
|
|
5010
4893
|
];
|
|
5011
4894
|
async function transpileTypeScript(filePath) {
|
|
5012
|
-
const content =
|
|
4895
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
5013
4896
|
const configDir = path8.dirname(path8.resolve(filePath));
|
|
5014
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*$/, "}");
|
|
5015
4898
|
transpiled = transpiled.replace(
|
|
@@ -5024,7 +4907,7 @@ async function transpileTypeScript(filePath) {
|
|
|
5024
4907
|
}
|
|
5025
4908
|
async function loadConfig(configPath) {
|
|
5026
4909
|
const absolutePath = path8.resolve(configPath);
|
|
5027
|
-
if (!
|
|
4910
|
+
if (!fs8.existsSync(absolutePath)) {
|
|
5028
4911
|
throw new Error(`Config file not found: ${absolutePath}`);
|
|
5029
4912
|
}
|
|
5030
4913
|
const isTypeScript = absolutePath.endsWith(".ts");
|
|
@@ -5034,7 +4917,7 @@ async function loadConfig(configPath) {
|
|
|
5034
4917
|
const transpiled = await transpileTypeScript(absolutePath);
|
|
5035
4918
|
const tempDir = os2.tmpdir();
|
|
5036
4919
|
const tempFile = path8.join(tempDir, `skedyul-config-${Date.now()}.cjs`);
|
|
5037
|
-
|
|
4920
|
+
fs8.writeFileSync(tempFile, transpiled);
|
|
5038
4921
|
moduleToLoad = tempFile;
|
|
5039
4922
|
try {
|
|
5040
4923
|
const module3 = require(moduleToLoad);
|
|
@@ -5048,7 +4931,7 @@ async function loadConfig(configPath) {
|
|
|
5048
4931
|
return config2;
|
|
5049
4932
|
} finally {
|
|
5050
4933
|
try {
|
|
5051
|
-
|
|
4934
|
+
fs8.unlinkSync(tempFile);
|
|
5052
4935
|
} catch {
|
|
5053
4936
|
}
|
|
5054
4937
|
}
|
|
@@ -5186,7 +5069,7 @@ Examples:
|
|
|
5186
5069
|
function findConfigFile2(startDir) {
|
|
5187
5070
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5188
5071
|
const filePath = path10.join(startDir, fileName);
|
|
5189
|
-
if (
|
|
5072
|
+
if (fs9.existsSync(filePath)) {
|
|
5190
5073
|
return filePath;
|
|
5191
5074
|
}
|
|
5192
5075
|
}
|
|
@@ -5222,7 +5105,7 @@ async function validateCommand(args2) {
|
|
|
5222
5105
|
} else {
|
|
5223
5106
|
configPath = path10.resolve(process.cwd(), configPath);
|
|
5224
5107
|
}
|
|
5225
|
-
if (!
|
|
5108
|
+
if (!fs9.existsSync(configPath)) {
|
|
5226
5109
|
const result2 = {
|
|
5227
5110
|
valid: false,
|
|
5228
5111
|
configPath,
|
|
@@ -5268,7 +5151,7 @@ async function validateCommand(args2) {
|
|
|
5268
5151
|
for (const workflow of provision.workflows) {
|
|
5269
5152
|
if (workflow.path) {
|
|
5270
5153
|
const absoluteWorkflowPath = path10.resolve(path10.dirname(configPath), workflow.path);
|
|
5271
|
-
if (!
|
|
5154
|
+
if (!fs9.existsSync(absoluteWorkflowPath)) {
|
|
5272
5155
|
warnings.push(`Workflow file not found: ${workflow.path}`);
|
|
5273
5156
|
}
|
|
5274
5157
|
}
|
|
@@ -5363,7 +5246,7 @@ async function validateCommand(args2) {
|
|
|
5363
5246
|
}
|
|
5364
5247
|
|
|
5365
5248
|
// src/cli/commands/diff.ts
|
|
5366
|
-
var
|
|
5249
|
+
var fs10 = __toESM(require("fs"));
|
|
5367
5250
|
var path11 = __toESM(require("path"));
|
|
5368
5251
|
init_utils();
|
|
5369
5252
|
function printHelp6() {
|
|
@@ -5398,7 +5281,7 @@ Examples:
|
|
|
5398
5281
|
function findConfigFile3(startDir) {
|
|
5399
5282
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5400
5283
|
const filePath = path11.join(startDir, fileName);
|
|
5401
|
-
if (
|
|
5284
|
+
if (fs10.existsSync(filePath)) {
|
|
5402
5285
|
return filePath;
|
|
5403
5286
|
}
|
|
5404
5287
|
}
|
|
@@ -5456,7 +5339,7 @@ async function diffCommand(args2) {
|
|
|
5456
5339
|
} else {
|
|
5457
5340
|
configPath = path11.resolve(process.cwd(), configPath);
|
|
5458
5341
|
}
|
|
5459
|
-
if (!
|
|
5342
|
+
if (!fs10.existsSync(configPath)) {
|
|
5460
5343
|
if (jsonOutput) {
|
|
5461
5344
|
console.log(JSON.stringify({ error: `Config file not found: ${configPath}` }));
|
|
5462
5345
|
} else {
|
|
@@ -5571,7 +5454,7 @@ async function diffCommand(args2) {
|
|
|
5571
5454
|
}
|
|
5572
5455
|
|
|
5573
5456
|
// src/cli/commands/deploy.ts
|
|
5574
|
-
var
|
|
5457
|
+
var fs11 = __toESM(require("fs"));
|
|
5575
5458
|
var path12 = __toESM(require("path"));
|
|
5576
5459
|
var readline2 = __toESM(require("readline"));
|
|
5577
5460
|
init_utils();
|
|
@@ -5609,7 +5492,7 @@ Examples:
|
|
|
5609
5492
|
function findConfigFile4(startDir) {
|
|
5610
5493
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
5611
5494
|
const filePath = path12.join(startDir, fileName);
|
|
5612
|
-
if (
|
|
5495
|
+
if (fs11.existsSync(filePath)) {
|
|
5613
5496
|
return filePath;
|
|
5614
5497
|
}
|
|
5615
5498
|
}
|
|
@@ -5725,7 +5608,7 @@ async function deployCommand(args2) {
|
|
|
5725
5608
|
} else {
|
|
5726
5609
|
configPath = path12.resolve(process.cwd(), configPath);
|
|
5727
5610
|
}
|
|
5728
|
-
if (!
|
|
5611
|
+
if (!fs11.existsSync(configPath)) {
|
|
5729
5612
|
if (jsonOutput) {
|
|
5730
5613
|
console.log(JSON.stringify({ error: `Config file not found: ${configPath}` }));
|
|
5731
5614
|
} else {
|
|
@@ -6042,7 +5925,7 @@ async function logoutCommand(args2) {
|
|
|
6042
5925
|
}
|
|
6043
5926
|
|
|
6044
5927
|
// src/cli/commands/auth/status.ts
|
|
6045
|
-
var
|
|
5928
|
+
var fs12 = __toESM(require("fs"));
|
|
6046
5929
|
var path13 = __toESM(require("path"));
|
|
6047
5930
|
init_utils();
|
|
6048
5931
|
function printHelp10() {
|
|
@@ -6121,13 +6004,13 @@ async function statusCommand(args2) {
|
|
|
6121
6004
|
console.log("");
|
|
6122
6005
|
console.log("LINKED WORKPLACES (this project)");
|
|
6123
6006
|
console.log("\u2500".repeat(60));
|
|
6124
|
-
if (
|
|
6125
|
-
const linkFiles =
|
|
6007
|
+
if (fs12.existsSync(linksDir)) {
|
|
6008
|
+
const linkFiles = fs12.readdirSync(linksDir).filter((f) => f.endsWith(".json"));
|
|
6126
6009
|
if (linkFiles.length > 0) {
|
|
6127
6010
|
for (const file2 of linkFiles) {
|
|
6128
6011
|
const subdomain = file2.replace(".json", "");
|
|
6129
6012
|
try {
|
|
6130
|
-
const content =
|
|
6013
|
+
const content = fs12.readFileSync(path13.join(linksDir, file2), "utf-8");
|
|
6131
6014
|
const link = JSON.parse(content);
|
|
6132
6015
|
console.log(` - ${subdomain} (${link.appHandle})`);
|
|
6133
6016
|
} catch {
|
|
@@ -6484,7 +6367,7 @@ EXAMPLES
|
|
|
6484
6367
|
}
|
|
6485
6368
|
|
|
6486
6369
|
// src/cli/commands/config-export.ts
|
|
6487
|
-
var
|
|
6370
|
+
var fs13 = __toESM(require("fs"));
|
|
6488
6371
|
var path14 = __toESM(require("path"));
|
|
6489
6372
|
function printHelp13() {
|
|
6490
6373
|
console.log(`
|
|
@@ -6532,7 +6415,7 @@ async function configExportCommand(args2) {
|
|
|
6532
6415
|
let configPath = null;
|
|
6533
6416
|
for (const name of CONFIG_FILE_NAMES) {
|
|
6534
6417
|
const testPath = path14.join(cwd, name);
|
|
6535
|
-
if (
|
|
6418
|
+
if (fs13.existsSync(testPath)) {
|
|
6536
6419
|
configPath = testPath;
|
|
6537
6420
|
break;
|
|
6538
6421
|
}
|
|
@@ -6547,11 +6430,11 @@ async function configExportCommand(args2) {
|
|
|
6547
6430
|
const resolvedConfig = await loadAndResolveConfig(configPath);
|
|
6548
6431
|
const serialized = serializeResolvedConfig(resolvedConfig);
|
|
6549
6432
|
const outputDir = path14.dirname(path14.resolve(cwd, outputPath));
|
|
6550
|
-
if (!
|
|
6551
|
-
|
|
6433
|
+
if (!fs13.existsSync(outputDir)) {
|
|
6434
|
+
fs13.mkdirSync(outputDir, { recursive: true });
|
|
6552
6435
|
}
|
|
6553
6436
|
const fullOutputPath = path14.resolve(cwd, outputPath);
|
|
6554
|
-
|
|
6437
|
+
fs13.writeFileSync(fullOutputPath, JSON.stringify(serialized, null, 2), "utf-8");
|
|
6555
6438
|
console.log(``);
|
|
6556
6439
|
console.log(`Config exported successfully!`);
|
|
6557
6440
|
console.log(` Output: ${outputPath}`);
|
|
@@ -7134,8 +7017,8 @@ async function handleCreateMany(modelHandle, flags) {
|
|
|
7134
7017
|
console.error("Usage: skedyul instances create-many <model> --file data.json --workplace <subdomain>");
|
|
7135
7018
|
process.exit(1);
|
|
7136
7019
|
}
|
|
7137
|
-
const
|
|
7138
|
-
const fileContent =
|
|
7020
|
+
const fs16 = await import("fs");
|
|
7021
|
+
const fileContent = fs16.readFileSync(filePath, "utf-8");
|
|
7139
7022
|
const items = JSON.parse(fileContent);
|
|
7140
7023
|
if (!Array.isArray(items)) {
|
|
7141
7024
|
console.error("Error: File must contain a JSON array of items");
|
|
@@ -7162,8 +7045,8 @@ async function handleUpsertMany(modelHandle, flags) {
|
|
|
7162
7045
|
console.error("Usage: skedyul instances upsert-many <model> --file data.json --match-field <field> --workplace <subdomain>");
|
|
7163
7046
|
process.exit(1);
|
|
7164
7047
|
}
|
|
7165
|
-
const
|
|
7166
|
-
const fileContent =
|
|
7048
|
+
const fs16 = await import("fs");
|
|
7049
|
+
const fileContent = fs16.readFileSync(filePath, "utf-8");
|
|
7167
7050
|
const items = JSON.parse(fileContent);
|
|
7168
7051
|
if (!Array.isArray(items)) {
|
|
7169
7052
|
console.error("Error: File must contain a JSON array of items");
|
|
@@ -7175,7 +7058,7 @@ async function handleUpsertMany(modelHandle, flags) {
|
|
|
7175
7058
|
|
|
7176
7059
|
// src/cli/commands/build.ts
|
|
7177
7060
|
var import_child_process = require("child_process");
|
|
7178
|
-
var
|
|
7061
|
+
var fs14 = __toESM(require("fs"));
|
|
7179
7062
|
var path15 = __toESM(require("path"));
|
|
7180
7063
|
function printBuildHelp() {
|
|
7181
7064
|
console.log(`
|
|
@@ -7246,7 +7129,7 @@ async function buildCommand(args2) {
|
|
|
7246
7129
|
let configPath = null;
|
|
7247
7130
|
for (const name of CONFIG_FILE_NAMES) {
|
|
7248
7131
|
const testPath = path15.join(cwd, name);
|
|
7249
|
-
if (
|
|
7132
|
+
if (fs14.existsSync(testPath)) {
|
|
7250
7133
|
configPath = testPath;
|
|
7251
7134
|
break;
|
|
7252
7135
|
}
|
|
@@ -7267,7 +7150,7 @@ async function buildCommand(args2) {
|
|
|
7267
7150
|
const userExternals = config.build && "external" in config.build ? config.build.external ?? [] : [];
|
|
7268
7151
|
const allExternals = [...baseExternals, ...userExternals];
|
|
7269
7152
|
const userTsupConfig = path15.join(cwd, "tsup.config.ts");
|
|
7270
|
-
const hasUserConfig =
|
|
7153
|
+
const hasUserConfig = fs14.existsSync(userTsupConfig);
|
|
7271
7154
|
console.log(``);
|
|
7272
7155
|
console.log(`Building ${config.name ?? "integration"}...`);
|
|
7273
7156
|
console.log(` Compute layer: ${computeLayer}`);
|
|
@@ -7287,7 +7170,7 @@ async function buildCommand(args2) {
|
|
|
7287
7170
|
}
|
|
7288
7171
|
} else {
|
|
7289
7172
|
const tsupConfigContent = generateTsupConfig(format, allExternals);
|
|
7290
|
-
|
|
7173
|
+
fs14.writeFileSync(tempConfigPath, tsupConfigContent, "utf-8");
|
|
7291
7174
|
createdTempConfig = true;
|
|
7292
7175
|
tsupArgs = [
|
|
7293
7176
|
"tsup",
|
|
@@ -7305,14 +7188,14 @@ async function buildCommand(args2) {
|
|
|
7305
7188
|
});
|
|
7306
7189
|
tsup.on("error", (error) => {
|
|
7307
7190
|
console.error("Failed to start tsup:", error.message);
|
|
7308
|
-
if (createdTempConfig &&
|
|
7309
|
-
|
|
7191
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7192
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7310
7193
|
}
|
|
7311
7194
|
process.exit(1);
|
|
7312
7195
|
});
|
|
7313
7196
|
tsup.on("close", (code) => {
|
|
7314
|
-
if (createdTempConfig &&
|
|
7315
|
-
|
|
7197
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7198
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7316
7199
|
}
|
|
7317
7200
|
if (code === 0) {
|
|
7318
7201
|
console.log(``);
|
|
@@ -7321,8 +7204,8 @@ async function buildCommand(args2) {
|
|
|
7321
7204
|
process.exit(code ?? 0);
|
|
7322
7205
|
});
|
|
7323
7206
|
} catch (error) {
|
|
7324
|
-
if (createdTempConfig &&
|
|
7325
|
-
|
|
7207
|
+
if (createdTempConfig && fs14.existsSync(tempConfigPath)) {
|
|
7208
|
+
fs14.unlinkSync(tempConfigPath);
|
|
7326
7209
|
}
|
|
7327
7210
|
console.error(
|
|
7328
7211
|
"Error loading config:",
|
|
@@ -7335,7 +7218,7 @@ async function buildCommand(args2) {
|
|
|
7335
7218
|
// src/cli/commands/smoke-test.ts
|
|
7336
7219
|
var import_child_process2 = require("child_process");
|
|
7337
7220
|
var http3 = __toESM(require("http"));
|
|
7338
|
-
var
|
|
7221
|
+
var fs15 = __toESM(require("fs"));
|
|
7339
7222
|
var path16 = __toESM(require("path"));
|
|
7340
7223
|
var SMOKE_TEST_PORT = 3456;
|
|
7341
7224
|
var HEALTH_CHECK_INTERVAL_MS = 500;
|
|
@@ -7462,7 +7345,7 @@ async function smokeTestCommand(args2) {
|
|
|
7462
7345
|
let configPath = null;
|
|
7463
7346
|
for (const name of CONFIG_FILE_NAMES) {
|
|
7464
7347
|
const testPath = path16.join(cwd, name);
|
|
7465
|
-
if (
|
|
7348
|
+
if (fs15.existsSync(testPath)) {
|
|
7466
7349
|
configPath = testPath;
|
|
7467
7350
|
break;
|
|
7468
7351
|
}
|
|
@@ -7478,10 +7361,10 @@ async function smokeTestCommand(args2) {
|
|
|
7478
7361
|
}
|
|
7479
7362
|
const serverExt = computeLayer === "serverless" ? "mjs" : "js";
|
|
7480
7363
|
let serverPath = `dist/server/mcp_server.${serverExt}`;
|
|
7481
|
-
if (!
|
|
7364
|
+
if (!fs15.existsSync(serverPath)) {
|
|
7482
7365
|
const fallbackExt = serverExt === "mjs" ? "js" : "mjs";
|
|
7483
7366
|
const fallbackPath = `dist/server/mcp_server.${fallbackExt}`;
|
|
7484
|
-
if (
|
|
7367
|
+
if (fs15.existsSync(fallbackPath)) {
|
|
7485
7368
|
console.warn(`[SmokeTest] Warning: Expected ${serverPath} but found ${fallbackPath}`);
|
|
7486
7369
|
console.warn(`[SmokeTest] Using ${fallbackPath} instead`);
|
|
7487
7370
|
serverPath = fallbackPath;
|