skedyul 1.2.23 → 1.2.25
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/commands/config-export.d.ts +12 -0
- package/dist/cli/index.js +301 -115
- package/dist/config/index.d.ts +1 -0
- package/dist/config/resolver.d.ts +38 -0
- package/dist/config/types/model.d.ts +1 -2
- package/dist/dedicated/server.js +21 -0
- package/dist/dockerfile.d.ts +1 -1
- package/dist/esm/index.mjs +57 -29
- package/dist/index.js +57 -29
- package/dist/server.js +21 -0
- package/dist/serverless/server.mjs +21 -0
- package/package.json +1 -1
package/dist/config/index.d.ts
CHANGED
|
@@ -13,4 +13,5 @@ export type { InstallConfig, ProvisionConfig, BuildConfig, CorsOptions, SkedyulC
|
|
|
13
13
|
export { defineConfig } from './app-config';
|
|
14
14
|
export { defineModel, defineChannel, definePage, defineWorkflow, defineAgent, defineEnv, defineNavigation, } from './define';
|
|
15
15
|
export { CONFIG_FILE_NAMES, loadConfig, validateConfig } from './loader';
|
|
16
|
+
export { loadAndResolveConfig, serializeResolvedConfig, type ResolvedConfig } from './resolver';
|
|
16
17
|
export { getAllEnvKeys, getRequiredInstallEnvKeys } from './utils';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config resolution utilities for build-time config export.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to fully resolve a skedyul.config.ts file,
|
|
5
|
+
* including all dynamic imports (tools, webhooks, provision).
|
|
6
|
+
*/
|
|
7
|
+
import type { SkedyulConfig, SerializableSkedyulConfig, ProvisionConfig } from './app-config';
|
|
8
|
+
import type { ToolRegistry, WebhookRegistry } from '../types';
|
|
9
|
+
/**
|
|
10
|
+
* Resolved config with all promises awaited.
|
|
11
|
+
*/
|
|
12
|
+
export interface ResolvedConfig extends Omit<SkedyulConfig, 'tools' | 'webhooks' | 'provision'> {
|
|
13
|
+
tools?: ToolRegistry;
|
|
14
|
+
webhooks?: WebhookRegistry;
|
|
15
|
+
provision?: ProvisionConfig;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load and fully resolve a skedyul.config.ts file.
|
|
19
|
+
*
|
|
20
|
+
* This function:
|
|
21
|
+
* 1. Dynamically imports the config file using tsx/node
|
|
22
|
+
* 2. Awaits all dynamic imports (tools, webhooks, provision)
|
|
23
|
+
* 3. Returns the fully resolved config
|
|
24
|
+
*
|
|
25
|
+
* @param configPath - Path to the skedyul.config.ts file
|
|
26
|
+
* @returns Fully resolved config with all promises awaited
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadAndResolveConfig(configPath: string): Promise<ResolvedConfig>;
|
|
29
|
+
/**
|
|
30
|
+
* Serialize a resolved config to the format stored in the database.
|
|
31
|
+
*
|
|
32
|
+
* This converts ToolRegistry and WebhookRegistry to their metadata-only forms,
|
|
33
|
+
* stripping out handler functions.
|
|
34
|
+
*
|
|
35
|
+
* @param config - Fully resolved config
|
|
36
|
+
* @returns Serializable config for database storage
|
|
37
|
+
*/
|
|
38
|
+
export declare function serializeResolvedConfig(config: ResolvedConfig): SerializableSkedyulConfig;
|
|
@@ -11,7 +11,6 @@ import type { ResourceDependency } from './resource';
|
|
|
11
11
|
* Field data types (lowercase).
|
|
12
12
|
* - 'string': Short text (single line)
|
|
13
13
|
* - 'long_string': Long text (multi-line, stored as text in DB)
|
|
14
|
-
* - 'text': Alias for long_string
|
|
15
14
|
* - 'number': Numeric value
|
|
16
15
|
* - 'boolean': True/false
|
|
17
16
|
* - 'date': Date only (no time)
|
|
@@ -22,7 +21,7 @@ import type { ResourceDependency } from './resource';
|
|
|
22
21
|
* - 'relation': Reference to another model
|
|
23
22
|
* - 'object': JSON object
|
|
24
23
|
*/
|
|
25
|
-
export type FieldType = 'string' | 'long_string' | '
|
|
24
|
+
export type FieldType = 'string' | 'long_string' | 'number' | 'boolean' | 'date' | 'datetime' | 'time' | 'file' | 'image' | 'relation' | 'object';
|
|
26
25
|
/**
|
|
27
26
|
* Behavior when a related record is deleted.
|
|
28
27
|
* - 'none': No action (orphan the reference)
|
package/dist/dedicated/server.js
CHANGED
|
@@ -485,6 +485,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
485
485
|
}
|
|
486
486
|
|
|
487
487
|
// src/server/dedicated.ts
|
|
488
|
+
var fs = __toESM(require("fs"));
|
|
488
489
|
var import_http2 = __toESM(require("http"));
|
|
489
490
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
490
491
|
|
|
@@ -1128,6 +1129,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
1128
1129
|
}
|
|
1129
1130
|
|
|
1130
1131
|
// src/server/dedicated.ts
|
|
1132
|
+
var CONFIG_FILE_PATH = ".skedyul/config.json";
|
|
1131
1133
|
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
|
|
1132
1134
|
const port = getListeningPort(config);
|
|
1133
1135
|
const registry = config.tools;
|
|
@@ -1148,6 +1150,15 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
1148
1150
|
return;
|
|
1149
1151
|
}
|
|
1150
1152
|
if (pathname === "/config" && req.method === "GET") {
|
|
1153
|
+
try {
|
|
1154
|
+
if (fs.existsSync(CONFIG_FILE_PATH)) {
|
|
1155
|
+
const fileConfig = JSON.parse(fs.readFileSync(CONFIG_FILE_PATH, "utf-8"));
|
|
1156
|
+
sendJSON(res, 200, fileConfig);
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
} catch (err) {
|
|
1160
|
+
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
1161
|
+
}
|
|
1151
1162
|
sendJSON(res, 200, serializeConfig(config));
|
|
1152
1163
|
return;
|
|
1153
1164
|
}
|
|
@@ -1460,6 +1471,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
1460
1471
|
}
|
|
1461
1472
|
|
|
1462
1473
|
// src/server/serverless.ts
|
|
1474
|
+
var fs2 = __toESM(require("fs"));
|
|
1475
|
+
var CONFIG_FILE_PATH2 = ".skedyul/config.json";
|
|
1463
1476
|
function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
1464
1477
|
const headers = getDefaultHeaders(config.cors);
|
|
1465
1478
|
const registry = config.tools;
|
|
@@ -1720,6 +1733,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
1720
1733
|
return createResponse(200, state.getHealthStatus(), headers);
|
|
1721
1734
|
}
|
|
1722
1735
|
if (path === "/config" && method === "GET") {
|
|
1736
|
+
try {
|
|
1737
|
+
if (fs2.existsSync(CONFIG_FILE_PATH2)) {
|
|
1738
|
+
const fileConfig = JSON.parse(fs2.readFileSync(CONFIG_FILE_PATH2, "utf-8"));
|
|
1739
|
+
return createResponse(200, fileConfig, headers);
|
|
1740
|
+
}
|
|
1741
|
+
} catch (err) {
|
|
1742
|
+
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
1743
|
+
}
|
|
1723
1744
|
return createResponse(200, serializeConfig(config), headers);
|
|
1724
1745
|
}
|
|
1725
1746
|
if (path === "/mcp" && method === "POST") {
|
package/dist/dockerfile.d.ts
CHANGED
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
* - BUILD_EXTERNAL: comma-separated list of external dependencies (e.g., 'twilio,stripe')
|
|
14
14
|
* - MCP_ENV_JSON: JSON string of environment variables to bake into the image
|
|
15
15
|
*/
|
|
16
|
-
export declare const DEFAULT_DOCKERFILE = "# =============================================================================\n# BUILDER STAGE - Common build for all targets\n# =============================================================================\nFROM public.ecr.aws/docker/library/node:22-alpine AS builder\n\nARG COMPUTE_LAYER=serverless\nARG BUILD_EXTERNAL=\"\"\nWORKDIR /app\n\n# Install pnpm\nRUN corepack enable && corepack prepare pnpm@latest --activate\n\n# Copy package files and source code\nCOPY package.json tsconfig.json skedyul.config.ts ./\nCOPY src ./src\n\n# Copy optional root-level TypeScript files (provision.ts, env.ts, etc.)\n# Using a wildcard that copies all .ts files from root except those in src/\n# Note: tsup.config.ts is optional - skedyul build generates it if not present\nCOPY *.ts ./\n\n# Install dependencies (including dev deps for build), compile, smoke test, then prune\n# Note: Using --no-frozen-lockfile since lockfile may not exist\n# skedyul build reads computeLayer from skedyul.config.ts\n# Smoke test runs before pruning since skedyul CLI is a dev dependency\nRUN pnpm install --no-frozen-lockfile && \\\n pnpm run build && \\\n pnpm exec skedyul smoke-test && \\\n pnpm prune --prod && \\\n pnpm store prune && \\\n rm -rf /tmp/* /var/cache/apk/* ~/.npm\n\n# =============================================================================\n# DEDICATED STAGE - For local Docker and ECS deployments (HTTP server)\n# =============================================================================\nFROM public.ecr.aws/docker/library/node:22-alpine AS dedicated\n\nWORKDIR /app\n\n# Copy built artifacts\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package.json ./package.json\n\n# Allow overriding the baked-in MCP env at runtime\nARG MCP_ENV_JSON=\"{}\"\nENV MCP_ENV_JSON=${MCP_ENV_JSON}\n\n# Expose the HTTP port\nEXPOSE 3000\n\n# Run as HTTP server (dedicated mode auto-detected by absence of AWS_LAMBDA_FUNCTION_NAME)\n# Support both .js (dedicated builds) and .mjs (serverless builds) extensions\nCMD [\"sh\", \"-c\", \"node dist/server/mcp_server.mjs 2>/dev/null || node dist/server/mcp_server.js\"]\n\n# =============================================================================\n# SERVERLESS STAGE - For AWS Lambda deployments\n# =============================================================================\nFROM public.ecr.aws/lambda/nodejs:22 AS serverless\n\nWORKDIR ${LAMBDA_TASK_ROOT}\n\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package.json ./package.json\n\n# Allow overriding the baked-in MCP env at runtime\nARG MCP_ENV_JSON=\"{}\"\nENV MCP_ENV_JSON=${MCP_ENV_JSON}\n\n# Lambda handler format\nCMD [\"dist/server/mcp_server.handler\"]\n\n# =============================================================================\n# DEFAULT - Use dedicated for local development, override with --target for production\n# =============================================================================\nFROM dedicated\n";
|
|
16
|
+
export declare const DEFAULT_DOCKERFILE = "# =============================================================================\n# BUILDER STAGE - Common build for all targets\n# =============================================================================\nFROM public.ecr.aws/docker/library/node:22-alpine AS builder\n\nARG COMPUTE_LAYER=serverless\nARG BUILD_EXTERNAL=\"\"\nWORKDIR /app\n\n# Install pnpm\nRUN corepack enable && corepack prepare pnpm@latest --activate\n\n# Copy package files and source code\nCOPY package.json tsconfig.json skedyul.config.ts ./\nCOPY src ./src\n\n# Copy optional root-level TypeScript files (provision.ts, env.ts, etc.)\n# Using a wildcard that copies all .ts files from root except those in src/\n# Note: tsup.config.ts is optional - skedyul build generates it if not present\nCOPY *.ts ./\n\n# Install dependencies (including dev deps for build), compile, export config, smoke test, then prune\n# Note: Using --no-frozen-lockfile since lockfile may not exist\n# skedyul build reads computeLayer from skedyul.config.ts\n# skedyul config:export resolves all dynamic imports and writes .skedyul/config.json\n# Smoke test runs before pruning since skedyul CLI is a dev dependency\nRUN pnpm install --no-frozen-lockfile && \\\n pnpm run build && \\\n pnpm exec skedyul config:export && \\\n pnpm exec skedyul smoke-test && \\\n pnpm prune --prod && \\\n pnpm store prune && \\\n rm -rf /tmp/* /var/cache/apk/* ~/.npm\n\n# =============================================================================\n# DEDICATED STAGE - For local Docker and ECS deployments (HTTP server)\n# =============================================================================\nFROM public.ecr.aws/docker/library/node:22-alpine AS dedicated\n\nWORKDIR /app\n\n# Copy built artifacts\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package.json ./package.json\nCOPY --from=builder /app/.skedyul ./.skedyul\n\n# Allow overriding the baked-in MCP env at runtime\nARG MCP_ENV_JSON=\"{}\"\nENV MCP_ENV_JSON=${MCP_ENV_JSON}\n\n# Expose the HTTP port\nEXPOSE 3000\n\n# Run as HTTP server (dedicated mode auto-detected by absence of AWS_LAMBDA_FUNCTION_NAME)\n# Support both .js (dedicated builds) and .mjs (serverless builds) extensions\nCMD [\"sh\", \"-c\", \"node dist/server/mcp_server.mjs 2>/dev/null || node dist/server/mcp_server.js\"]\n\n# =============================================================================\n# SERVERLESS STAGE - For AWS Lambda deployments\n# =============================================================================\nFROM public.ecr.aws/lambda/nodejs:22 AS serverless\n\nWORKDIR ${LAMBDA_TASK_ROOT}\n\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package.json ./package.json\nCOPY --from=builder /app/.skedyul ./.skedyul\n\n# Allow overriding the baked-in MCP env at runtime\nARG MCP_ENV_JSON=\"{}\"\nENV MCP_ENV_JSON=${MCP_ENV_JSON}\n\n# Lambda handler format\nCMD [\"dist/server/mcp_server.handler\"]\n\n# =============================================================================\n# DEFAULT - Use dedicated for local development, override with --target for production\n# =============================================================================\nFROM dedicated\n";
|
package/dist/esm/index.mjs
CHANGED
|
@@ -900,13 +900,13 @@ function mergeRuntimeEnv() {
|
|
|
900
900
|
|
|
901
901
|
// src/server/utils/http.ts
|
|
902
902
|
function readRawRequestBody(req) {
|
|
903
|
-
return new Promise((
|
|
903
|
+
return new Promise((resolve3, reject) => {
|
|
904
904
|
let body = "";
|
|
905
905
|
req.on("data", (chunk) => {
|
|
906
906
|
body += chunk.toString();
|
|
907
907
|
});
|
|
908
908
|
req.on("end", () => {
|
|
909
|
-
|
|
909
|
+
resolve3(body);
|
|
910
910
|
});
|
|
911
911
|
req.on("error", reject);
|
|
912
912
|
});
|
|
@@ -2197,6 +2197,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
2197
2197
|
}
|
|
2198
2198
|
|
|
2199
2199
|
// src/server/dedicated.ts
|
|
2200
|
+
import * as fs from "fs";
|
|
2200
2201
|
import http from "http";
|
|
2201
2202
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
2202
2203
|
|
|
@@ -2729,7 +2730,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
|
|
|
2729
2730
|
}
|
|
2730
2731
|
|
|
2731
2732
|
// src/server/handlers/webhook-handler.ts
|
|
2732
|
-
function parseWebhookRequest(parsedBody, method, url,
|
|
2733
|
+
function parseWebhookRequest(parsedBody, method, url, path3, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
|
|
2733
2734
|
const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
|
|
2734
2735
|
if (isEnvelope) {
|
|
2735
2736
|
const envelope = parsedBody;
|
|
@@ -2783,7 +2784,7 @@ function parseWebhookRequest(parsedBody, method, url, path2, headers, query, raw
|
|
|
2783
2784
|
const webhookRequest = {
|
|
2784
2785
|
method,
|
|
2785
2786
|
url,
|
|
2786
|
-
path:
|
|
2787
|
+
path: path3,
|
|
2787
2788
|
headers,
|
|
2788
2789
|
query,
|
|
2789
2790
|
body: parsedBody,
|
|
@@ -2840,6 +2841,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
2840
2841
|
}
|
|
2841
2842
|
|
|
2842
2843
|
// src/server/dedicated.ts
|
|
2844
|
+
var CONFIG_FILE_PATH = ".skedyul/config.json";
|
|
2843
2845
|
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
|
|
2844
2846
|
const port = getListeningPort(config);
|
|
2845
2847
|
const registry = config.tools;
|
|
@@ -2860,6 +2862,15 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
2860
2862
|
return;
|
|
2861
2863
|
}
|
|
2862
2864
|
if (pathname === "/config" && req.method === "GET") {
|
|
2865
|
+
try {
|
|
2866
|
+
if (fs.existsSync(CONFIG_FILE_PATH)) {
|
|
2867
|
+
const fileConfig = JSON.parse(fs.readFileSync(CONFIG_FILE_PATH, "utf-8"));
|
|
2868
|
+
sendJSON(res, 200, fileConfig);
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
} catch (err) {
|
|
2872
|
+
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
2873
|
+
}
|
|
2863
2874
|
sendJSON(res, 200, serializeConfig(config));
|
|
2864
2875
|
return;
|
|
2865
2876
|
}
|
|
@@ -3159,10 +3170,10 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
3159
3170
|
return {
|
|
3160
3171
|
async listen(listenPort) {
|
|
3161
3172
|
const finalPort = listenPort ?? port;
|
|
3162
|
-
return new Promise((
|
|
3173
|
+
return new Promise((resolve3, reject) => {
|
|
3163
3174
|
httpServer.listen(finalPort, () => {
|
|
3164
3175
|
printStartupLog(config, tools, finalPort);
|
|
3165
|
-
|
|
3176
|
+
resolve3();
|
|
3166
3177
|
});
|
|
3167
3178
|
httpServer.once("error", reject);
|
|
3168
3179
|
});
|
|
@@ -3172,6 +3183,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
3172
3183
|
}
|
|
3173
3184
|
|
|
3174
3185
|
// src/server/serverless.ts
|
|
3186
|
+
import * as fs2 from "fs";
|
|
3187
|
+
var CONFIG_FILE_PATH2 = ".skedyul/config.json";
|
|
3175
3188
|
function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
3176
3189
|
const headers = getDefaultHeaders(config.cors);
|
|
3177
3190
|
const registry = config.tools;
|
|
@@ -3184,13 +3197,13 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3184
3197
|
hasLoggedStartup = true;
|
|
3185
3198
|
}
|
|
3186
3199
|
try {
|
|
3187
|
-
const
|
|
3200
|
+
const path3 = event.path || event.rawPath || "/";
|
|
3188
3201
|
const method = event.httpMethod || event.requestContext?.http?.method || "POST";
|
|
3189
3202
|
if (method === "OPTIONS") {
|
|
3190
3203
|
return createResponse(200, { message: "OK" }, headers);
|
|
3191
3204
|
}
|
|
3192
|
-
if (
|
|
3193
|
-
const handle =
|
|
3205
|
+
if (path3.startsWith("/webhooks/") && webhookRegistry) {
|
|
3206
|
+
const handle = path3.slice("/webhooks/".length);
|
|
3194
3207
|
if (!webhookRegistry[handle]) {
|
|
3195
3208
|
return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
|
|
3196
3209
|
}
|
|
@@ -3213,12 +3226,12 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3213
3226
|
const protocol = forwardedProto ?? "https";
|
|
3214
3227
|
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3215
3228
|
const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
|
|
3216
|
-
const webhookUrl = `${protocol}://${host}${
|
|
3229
|
+
const webhookUrl = `${protocol}://${host}${path3}${queryString}`;
|
|
3217
3230
|
const parseResult = parseWebhookRequest(
|
|
3218
3231
|
parsedBody,
|
|
3219
3232
|
method,
|
|
3220
3233
|
webhookUrl,
|
|
3221
|
-
|
|
3234
|
+
path3,
|
|
3222
3235
|
event.headers,
|
|
3223
3236
|
event.queryStringParameters ?? {},
|
|
3224
3237
|
rawBody,
|
|
@@ -3239,7 +3252,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3239
3252
|
body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
|
|
3240
3253
|
};
|
|
3241
3254
|
}
|
|
3242
|
-
if (
|
|
3255
|
+
if (path3 === "/core" && method === "POST") {
|
|
3243
3256
|
let coreBody;
|
|
3244
3257
|
try {
|
|
3245
3258
|
coreBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3271,7 +3284,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3271
3284
|
const result = await handleCoreMethod(coreMethod, coreBody.params);
|
|
3272
3285
|
return createResponse(result.status, result.payload, headers);
|
|
3273
3286
|
}
|
|
3274
|
-
if (
|
|
3287
|
+
if (path3 === "/core/webhook" && method === "POST") {
|
|
3275
3288
|
const rawWebhookBody = event.body ?? "";
|
|
3276
3289
|
let webhookBody;
|
|
3277
3290
|
try {
|
|
@@ -3286,14 +3299,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3286
3299
|
const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
|
|
3287
3300
|
const protocol = forwardedProto ?? "https";
|
|
3288
3301
|
const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
|
|
3289
|
-
const webhookUrl = `${protocol}://${host}${
|
|
3302
|
+
const webhookUrl = `${protocol}://${host}${path3}`;
|
|
3290
3303
|
const coreWebhookRequest = {
|
|
3291
3304
|
method,
|
|
3292
3305
|
headers: event.headers ?? {},
|
|
3293
3306
|
body: webhookBody,
|
|
3294
3307
|
query: event.queryStringParameters ?? {},
|
|
3295
3308
|
url: webhookUrl,
|
|
3296
|
-
path:
|
|
3309
|
+
path: path3,
|
|
3297
3310
|
rawBody: rawWebhookBody ? Buffer.from(rawWebhookBody, "utf-8") : void 0
|
|
3298
3311
|
};
|
|
3299
3312
|
const webhookResponse = await coreApiService.dispatchWebhook(
|
|
@@ -3305,7 +3318,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3305
3318
|
headers
|
|
3306
3319
|
);
|
|
3307
3320
|
}
|
|
3308
|
-
if (
|
|
3321
|
+
if (path3 === "/estimate" && method === "POST") {
|
|
3309
3322
|
let estimateBody;
|
|
3310
3323
|
try {
|
|
3311
3324
|
estimateBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3371,7 +3384,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3371
3384
|
);
|
|
3372
3385
|
}
|
|
3373
3386
|
}
|
|
3374
|
-
if (
|
|
3387
|
+
if (path3 === "/install" && method === "POST") {
|
|
3375
3388
|
let installBody;
|
|
3376
3389
|
try {
|
|
3377
3390
|
installBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3385,7 +3398,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3385
3398
|
const result = await handleInstall(installBody, config.hooks);
|
|
3386
3399
|
return createResponse(result.status, result.body, headers);
|
|
3387
3400
|
}
|
|
3388
|
-
if (
|
|
3401
|
+
if (path3 === "/uninstall" && method === "POST") {
|
|
3389
3402
|
let uninstallBody;
|
|
3390
3403
|
try {
|
|
3391
3404
|
uninstallBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3399,7 +3412,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3399
3412
|
const result = await handleUninstall(uninstallBody, config.hooks);
|
|
3400
3413
|
return createResponse(result.status, result.body, headers);
|
|
3401
3414
|
}
|
|
3402
|
-
if (
|
|
3415
|
+
if (path3 === "/provision" && method === "POST") {
|
|
3403
3416
|
let provisionBody;
|
|
3404
3417
|
try {
|
|
3405
3418
|
provisionBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3413,7 +3426,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3413
3426
|
const result = await handleProvision(provisionBody, config.hooks);
|
|
3414
3427
|
return createResponse(result.status, result.body, headers);
|
|
3415
3428
|
}
|
|
3416
|
-
if (
|
|
3429
|
+
if (path3 === "/oauth_callback" && method === "POST") {
|
|
3417
3430
|
let parsedBody;
|
|
3418
3431
|
try {
|
|
3419
3432
|
parsedBody = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3428,13 +3441,21 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
3428
3441
|
const result = await handleOAuthCallback(parsedBody, config.hooks);
|
|
3429
3442
|
return createResponse(result.status, result.body, headers);
|
|
3430
3443
|
}
|
|
3431
|
-
if (
|
|
3444
|
+
if (path3 === "/health" && method === "GET") {
|
|
3432
3445
|
return createResponse(200, state.getHealthStatus(), headers);
|
|
3433
3446
|
}
|
|
3434
|
-
if (
|
|
3447
|
+
if (path3 === "/config" && method === "GET") {
|
|
3448
|
+
try {
|
|
3449
|
+
if (fs2.existsSync(CONFIG_FILE_PATH2)) {
|
|
3450
|
+
const fileConfig = JSON.parse(fs2.readFileSync(CONFIG_FILE_PATH2, "utf-8"));
|
|
3451
|
+
return createResponse(200, fileConfig, headers);
|
|
3452
|
+
}
|
|
3453
|
+
} catch (err) {
|
|
3454
|
+
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
3455
|
+
}
|
|
3435
3456
|
return createResponse(200, serializeConfig(config), headers);
|
|
3436
3457
|
}
|
|
3437
|
-
if (
|
|
3458
|
+
if (path3 === "/mcp" && method === "POST") {
|
|
3438
3459
|
let body;
|
|
3439
3460
|
try {
|
|
3440
3461
|
body = event.body ? JSON.parse(event.body) : {};
|
|
@@ -3842,12 +3863,14 @@ COPY src ./src
|
|
|
3842
3863
|
# Note: tsup.config.ts is optional - skedyul build generates it if not present
|
|
3843
3864
|
COPY *.ts ./
|
|
3844
3865
|
|
|
3845
|
-
# Install dependencies (including dev deps for build), compile, smoke test, then prune
|
|
3866
|
+
# Install dependencies (including dev deps for build), compile, export config, smoke test, then prune
|
|
3846
3867
|
# Note: Using --no-frozen-lockfile since lockfile may not exist
|
|
3847
3868
|
# skedyul build reads computeLayer from skedyul.config.ts
|
|
3869
|
+
# skedyul config:export resolves all dynamic imports and writes .skedyul/config.json
|
|
3848
3870
|
# Smoke test runs before pruning since skedyul CLI is a dev dependency
|
|
3849
3871
|
RUN pnpm install --no-frozen-lockfile && \\
|
|
3850
3872
|
pnpm run build && \\
|
|
3873
|
+
pnpm exec skedyul config:export && \\
|
|
3851
3874
|
pnpm exec skedyul smoke-test && \\
|
|
3852
3875
|
pnpm prune --prod && \\
|
|
3853
3876
|
pnpm store prune && \\
|
|
@@ -3864,6 +3887,7 @@ WORKDIR /app
|
|
|
3864
3887
|
COPY --from=builder /app/node_modules ./node_modules
|
|
3865
3888
|
COPY --from=builder /app/dist ./dist
|
|
3866
3889
|
COPY --from=builder /app/package.json ./package.json
|
|
3890
|
+
COPY --from=builder /app/.skedyul ./.skedyul
|
|
3867
3891
|
|
|
3868
3892
|
# Allow overriding the baked-in MCP env at runtime
|
|
3869
3893
|
ARG MCP_ENV_JSON="{}"
|
|
@@ -3886,6 +3910,7 @@ WORKDIR \${LAMBDA_TASK_ROOT}
|
|
|
3886
3910
|
COPY --from=builder /app/node_modules ./node_modules
|
|
3887
3911
|
COPY --from=builder /app/dist ./dist
|
|
3888
3912
|
COPY --from=builder /app/package.json ./package.json
|
|
3913
|
+
COPY --from=builder /app/.skedyul ./.skedyul
|
|
3889
3914
|
|
|
3890
3915
|
# Allow overriding the baked-in MCP env at runtime
|
|
3891
3916
|
ARG MCP_ENV_JSON="{}"
|
|
@@ -3929,7 +3954,7 @@ function defineNavigation(navigation) {
|
|
|
3929
3954
|
}
|
|
3930
3955
|
|
|
3931
3956
|
// src/config/loader.ts
|
|
3932
|
-
import * as
|
|
3957
|
+
import * as fs3 from "fs";
|
|
3933
3958
|
import * as path from "path";
|
|
3934
3959
|
import * as os from "os";
|
|
3935
3960
|
var CONFIG_FILE_NAMES = [
|
|
@@ -3939,7 +3964,7 @@ var CONFIG_FILE_NAMES = [
|
|
|
3939
3964
|
"skedyul.config.cjs"
|
|
3940
3965
|
];
|
|
3941
3966
|
async function transpileTypeScript(filePath) {
|
|
3942
|
-
const content =
|
|
3967
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
3943
3968
|
const configDir = path.dirname(path.resolve(filePath));
|
|
3944
3969
|
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*$/, "}");
|
|
3945
3970
|
transpiled = transpiled.replace(
|
|
@@ -3954,7 +3979,7 @@ async function transpileTypeScript(filePath) {
|
|
|
3954
3979
|
}
|
|
3955
3980
|
async function loadConfig(configPath) {
|
|
3956
3981
|
const absolutePath = path.resolve(configPath);
|
|
3957
|
-
if (!
|
|
3982
|
+
if (!fs3.existsSync(absolutePath)) {
|
|
3958
3983
|
throw new Error(`Config file not found: ${absolutePath}`);
|
|
3959
3984
|
}
|
|
3960
3985
|
const isTypeScript = absolutePath.endsWith(".ts");
|
|
@@ -3964,7 +3989,7 @@ async function loadConfig(configPath) {
|
|
|
3964
3989
|
const transpiled = await transpileTypeScript(absolutePath);
|
|
3965
3990
|
const tempDir = os.tmpdir();
|
|
3966
3991
|
const tempFile = path.join(tempDir, `skedyul-config-${Date.now()}.js`);
|
|
3967
|
-
|
|
3992
|
+
fs3.writeFileSync(tempFile, transpiled);
|
|
3968
3993
|
moduleToLoad = tempFile;
|
|
3969
3994
|
try {
|
|
3970
3995
|
const module2 = __require(moduleToLoad);
|
|
@@ -3978,7 +4003,7 @@ async function loadConfig(configPath) {
|
|
|
3978
4003
|
return config2;
|
|
3979
4004
|
} finally {
|
|
3980
4005
|
try {
|
|
3981
|
-
|
|
4006
|
+
fs3.unlinkSync(tempFile);
|
|
3982
4007
|
} catch {
|
|
3983
4008
|
}
|
|
3984
4009
|
}
|
|
@@ -4009,6 +4034,9 @@ function validateConfig(config) {
|
|
|
4009
4034
|
return { valid: errors.length === 0, errors };
|
|
4010
4035
|
}
|
|
4011
4036
|
|
|
4037
|
+
// src/config/resolver.ts
|
|
4038
|
+
import * as path2 from "path";
|
|
4039
|
+
|
|
4012
4040
|
// src/config/utils.ts
|
|
4013
4041
|
function getAllEnvKeys(config) {
|
|
4014
4042
|
const provision = config.provision && "env" in config.provision ? config.provision : void 0;
|