skedyul 1.2.33 → 1.2.35

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 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
- function isMethodAllowed(webhookRegistry, handle, method) {
3333
- const webhookDef = webhookRegistry[handle];
3334
- if (!webhookDef) return false;
3335
- const allowedMethods = webhookDef.methods ?? ["POST"];
3336
- return allowedMethods.includes(method);
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
- var CONFIG_FILE_PATH = ".skedyul/config.json";
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 === "/health" && req.method === "GET") {
3357
- sendJSON(res, 200, state.getHealthStatus());
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
- if (pathname === "/mcp" && req.method === "POST") {
3595
- try {
3596
- const body = await parseJSONBody(req);
3597
- if (body?.method === "tools/call") {
3598
- console.log("[dedicated.ts /mcp] Received tools/call request:", JSON.stringify({
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
- sendJSON(res, 404, {
3652
- jsonrpc: "2.0",
3653
- id: null,
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 headers = getDefaultHeaders(config.cors);
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
- try {
3702
- const path17 = event.path || event.rawPath || "/";
3703
- const method = event.httpMethod || event.requestContext?.http?.method || "POST";
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 fs10 = __toESM(require("fs"));
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 fs9 = __toESM(require("fs"));
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 = fs9.readFileSync(filePath, "utf-8");
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 (!fs9.existsSync(absolutePath)) {
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
- fs9.writeFileSync(tempFile, transpiled);
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
- fs9.unlinkSync(tempFile);
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 (fs10.existsSync(filePath)) {
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 (!fs10.existsSync(configPath)) {
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 (!fs10.existsSync(absoluteWorkflowPath)) {
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 fs11 = __toESM(require("fs"));
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 (fs11.existsSync(filePath)) {
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 (!fs11.existsSync(configPath)) {
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 fs12 = __toESM(require("fs"));
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 (fs12.existsSync(filePath)) {
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 (!fs12.existsSync(configPath)) {
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 fs13 = __toESM(require("fs"));
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 (fs13.existsSync(linksDir)) {
6125
- const linkFiles = fs13.readdirSync(linksDir).filter((f) => f.endsWith(".json"));
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 = fs13.readFileSync(path13.join(linksDir, file2), "utf-8");
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 fs14 = __toESM(require("fs"));
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 (fs14.existsSync(testPath)) {
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 (!fs14.existsSync(outputDir)) {
6551
- fs14.mkdirSync(outputDir, { recursive: true });
6433
+ if (!fs13.existsSync(outputDir)) {
6434
+ fs13.mkdirSync(outputDir, { recursive: true });
6552
6435
  }
6553
6436
  const fullOutputPath = path14.resolve(cwd, outputPath);
6554
- fs14.writeFileSync(fullOutputPath, JSON.stringify(serialized, null, 2), "utf-8");
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 fs17 = await import("fs");
7138
- const fileContent = fs17.readFileSync(filePath, "utf-8");
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 fs17 = await import("fs");
7166
- const fileContent = fs17.readFileSync(filePath, "utf-8");
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 fs15 = __toESM(require("fs"));
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 (fs15.existsSync(testPath)) {
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 = fs15.existsSync(userTsupConfig);
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
- fs15.writeFileSync(tempConfigPath, tsupConfigContent, "utf-8");
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 && fs15.existsSync(tempConfigPath)) {
7309
- fs15.unlinkSync(tempConfigPath);
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 && fs15.existsSync(tempConfigPath)) {
7315
- fs15.unlinkSync(tempConfigPath);
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 && fs15.existsSync(tempConfigPath)) {
7325
- fs15.unlinkSync(tempConfigPath);
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 fs16 = __toESM(require("fs"));
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 (fs16.existsSync(testPath)) {
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 (!fs16.existsSync(serverPath)) {
7364
+ if (!fs15.existsSync(serverPath)) {
7482
7365
  const fallbackExt = serverExt === "mjs" ? "js" : "mjs";
7483
7366
  const fallbackPath = `dist/server/mcp_server.${fallbackExt}`;
7484
- if (fs16.existsSync(fallbackPath)) {
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;