skedyul 1.1.3 → 1.1.7

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.
@@ -0,0 +1 @@
1
+ export declare function smokeTestCommand(args: string[]): Promise<void>;
package/dist/cli/index.js CHANGED
@@ -2280,11 +2280,11 @@ var CoreApiService = class {
2280
2280
  setWebhookHandler(handler) {
2281
2281
  this.webhookHandler = handler;
2282
2282
  }
2283
- async dispatchWebhook(request) {
2283
+ async dispatchWebhook(request2) {
2284
2284
  if (!this.webhookHandler) {
2285
2285
  return { status: 404 };
2286
2286
  }
2287
- return this.webhookHandler(request);
2287
+ return this.webhookHandler(request2);
2288
2288
  }
2289
2289
  async callCreateChannel(channel) {
2290
2290
  return this.service?.createCommunicationChannel(channel);
@@ -2600,25 +2600,25 @@ function createCallToolHandler(registry, state, onMaxRequests) {
2600
2600
  };
2601
2601
  } else {
2602
2602
  const workplace = rawContext.workplace;
2603
- const request = rawContext.request;
2603
+ const request2 = rawContext.request;
2604
2604
  const appInstallationId = rawContext.appInstallationId;
2605
2605
  const envVars = process.env;
2606
2606
  const modeValue = estimateMode ? "estimate" : "execute";
2607
2607
  if (trigger === "field_change") {
2608
2608
  const field = rawContext.field;
2609
- executionContext = { trigger: "field_change", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, field, invocation, log };
2609
+ executionContext = { trigger: "field_change", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, field, invocation, log };
2610
2610
  } else if (trigger === "page_action") {
2611
2611
  const page = rawContext.page;
2612
- executionContext = { trigger: "page_action", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, page, invocation, log };
2612
+ executionContext = { trigger: "page_action", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, page, invocation, log };
2613
2613
  } else if (trigger === "form_submit") {
2614
2614
  const form = rawContext.form;
2615
- executionContext = { trigger: "form_submit", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, form, invocation, log };
2615
+ executionContext = { trigger: "form_submit", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, form, invocation, log };
2616
2616
  } else if (trigger === "workflow") {
2617
- executionContext = { trigger: "workflow", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation, log };
2617
+ executionContext = { trigger: "workflow", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, invocation, log };
2618
2618
  } else if (trigger === "page_context") {
2619
- executionContext = { trigger: "agent", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation, log };
2619
+ executionContext = { trigger: "agent", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, invocation, log };
2620
2620
  } else {
2621
- executionContext = { trigger: "agent", app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation, log };
2621
+ executionContext = { trigger: "agent", app, appInstallationId, workplace, request: request2, env: envVars, mode: modeValue, invocation, log };
2622
2622
  }
2623
2623
  }
2624
2624
  const requestConfig = {
@@ -7019,8 +7019,8 @@ async function handleCreateMany(modelHandle, flags) {
7019
7019
  console.error("Usage: skedyul instances create-many <model> --file data.json --workplace <subdomain>");
7020
7020
  process.exit(1);
7021
7021
  }
7022
- const fs13 = await import("fs");
7023
- const fileContent = fs13.readFileSync(filePath, "utf-8");
7022
+ const fs14 = await import("fs");
7023
+ const fileContent = fs14.readFileSync(filePath, "utf-8");
7024
7024
  const items = JSON.parse(fileContent);
7025
7025
  if (!Array.isArray(items)) {
7026
7026
  console.error("Error: File must contain a JSON array of items");
@@ -7047,8 +7047,8 @@ async function handleUpsertMany(modelHandle, flags) {
7047
7047
  console.error("Usage: skedyul instances upsert-many <model> --file data.json --match-field <field> --workplace <subdomain>");
7048
7048
  process.exit(1);
7049
7049
  }
7050
- const fs13 = await import("fs");
7051
- const fileContent = fs13.readFileSync(filePath, "utf-8");
7050
+ const fs14 = await import("fs");
7051
+ const fileContent = fs14.readFileSync(filePath, "utf-8");
7052
7052
  const items = JSON.parse(fileContent);
7053
7053
  if (!Array.isArray(items)) {
7054
7054
  console.error("Error: File must contain a JSON array of items");
@@ -7124,7 +7124,7 @@ async function buildCommand(args2) {
7124
7124
  console.log(`Loading config from ${path12.basename(configPath)}...`);
7125
7125
  try {
7126
7126
  const config = await loadConfig(configPath);
7127
- const computeLayer = config.computeLayer ?? "serverless";
7127
+ const computeLayer = process.env.COMPUTE_LAYER ?? config.computeLayer ?? "serverless";
7128
7128
  const format = computeLayer === "serverless" ? "esm" : "cjs";
7129
7129
  const baseExternals = ["skedyul", `skedyul/${computeLayer}`, "zod"];
7130
7130
  const userExternals = config.build && "external" in config.build ? config.build.external ?? [] : [];
@@ -7176,6 +7176,242 @@ async function buildCommand(args2) {
7176
7176
  }
7177
7177
  }
7178
7178
 
7179
+ // src/cli/commands/smoke-test.ts
7180
+ var import_child_process2 = require("child_process");
7181
+ var http3 = __toESM(require("http"));
7182
+ var fs13 = __toESM(require("fs"));
7183
+ var SMOKE_TEST_PORT = 3456;
7184
+ var HEALTH_CHECK_INTERVAL_MS = 500;
7185
+ var HEALTH_CHECK_MAX_RETRIES = 30;
7186
+ function printSmokeTestHelp() {
7187
+ console.log(`
7188
+ SKEDYUL SMOKE-TEST - Validate your built integration
7189
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
7190
+
7191
+ Spawns the built server and validates it responds to tools/list.
7192
+ This catches module-level errors that would crash the server on startup.
7193
+
7194
+ USAGE
7195
+ $ skedyul smoke-test [options]
7196
+
7197
+ OPTIONS
7198
+ --help, -h Show this help message
7199
+
7200
+ EXAMPLES
7201
+ # Run smoke test after building
7202
+ $ skedyul build && skedyul smoke-test
7203
+
7204
+ WHAT IT DOES
7205
+ 1. Spawns node dist/server/mcp_server.js as a child process
7206
+ 2. Waits for the /health endpoint to respond
7207
+ 3. Calls POST /mcp with tools/list JSON-RPC request
7208
+ 4. Validates the response contains at least one tool
7209
+ 5. Exits with code 0 (success) or 1 (failure)
7210
+ `);
7211
+ }
7212
+ function makeRequest(port, path13, method, body) {
7213
+ return new Promise((resolve8, reject) => {
7214
+ const postData = body ? JSON.stringify(body) : void 0;
7215
+ const options = {
7216
+ hostname: "localhost",
7217
+ port,
7218
+ path: path13,
7219
+ method,
7220
+ headers: {
7221
+ "Content-Type": "application/json",
7222
+ Accept: "application/json",
7223
+ ...postData ? { "Content-Length": Buffer.byteLength(postData) } : {}
7224
+ }
7225
+ };
7226
+ const req = http3.request(options, (res) => {
7227
+ let data = "";
7228
+ res.on("data", (chunk) => {
7229
+ data += chunk;
7230
+ });
7231
+ res.on("end", () => {
7232
+ try {
7233
+ const parsed = data ? JSON.parse(data) : {};
7234
+ resolve8({ status: res.statusCode ?? 0, body: parsed });
7235
+ } catch {
7236
+ resolve8({ status: res.statusCode ?? 0, body: data });
7237
+ }
7238
+ });
7239
+ });
7240
+ req.on("error", reject);
7241
+ if (postData) {
7242
+ req.write(postData);
7243
+ }
7244
+ req.end();
7245
+ });
7246
+ }
7247
+ async function waitForHealth(port) {
7248
+ for (let i = 0; i < HEALTH_CHECK_MAX_RETRIES; i++) {
7249
+ try {
7250
+ const response = await makeRequest(port, "/health", "GET");
7251
+ if (response.status === 200) {
7252
+ return true;
7253
+ }
7254
+ } catch {
7255
+ }
7256
+ await new Promise((resolve8) => setTimeout(resolve8, HEALTH_CHECK_INTERVAL_MS));
7257
+ }
7258
+ return false;
7259
+ }
7260
+ async function callToolsList(port) {
7261
+ try {
7262
+ const response = await makeRequest(port, "/mcp", "POST", {
7263
+ jsonrpc: "2.0",
7264
+ id: 1,
7265
+ method: "tools/list",
7266
+ params: {}
7267
+ });
7268
+ if (response.status !== 200) {
7269
+ return {
7270
+ success: false,
7271
+ error: `tools/list returned status ${response.status}`
7272
+ };
7273
+ }
7274
+ const body = response.body;
7275
+ if (body.error) {
7276
+ return {
7277
+ success: false,
7278
+ error: `tools/list error: ${body.error.message ?? JSON.stringify(body.error)}`
7279
+ };
7280
+ }
7281
+ if (!body.result?.tools) {
7282
+ return {
7283
+ success: false,
7284
+ error: "tools/list response missing result.tools"
7285
+ };
7286
+ }
7287
+ return {
7288
+ success: true,
7289
+ tools: body.result.tools
7290
+ };
7291
+ } catch (err) {
7292
+ return {
7293
+ success: false,
7294
+ error: `Failed to call tools/list: ${err instanceof Error ? err.message : String(err)}`
7295
+ };
7296
+ }
7297
+ }
7298
+ async function smokeTestCommand(args2) {
7299
+ if (args2.includes("--help") || args2.includes("-h")) {
7300
+ printSmokeTestHelp();
7301
+ process.exit(0);
7302
+ }
7303
+ const serverPath = "dist/server/mcp_server.js";
7304
+ if (!fs13.existsSync(serverPath)) {
7305
+ console.error("[SmokeTest] ERROR: dist/server/mcp_server.js not found");
7306
+ console.error('[SmokeTest] Run "skedyul build" first');
7307
+ process.exit(1);
7308
+ }
7309
+ console.log("[SmokeTest] Starting smoke test...");
7310
+ console.log(`[SmokeTest] Server path: ${serverPath}`);
7311
+ console.log(`[SmokeTest] Port: ${SMOKE_TEST_PORT}`);
7312
+ let server2 = null;
7313
+ let serverLogs = [];
7314
+ let serverExited = false;
7315
+ let serverExitCode = null;
7316
+ const cleanup = () => {
7317
+ if (server2 && !serverExited) {
7318
+ console.log("[SmokeTest] Stopping server...");
7319
+ server2.kill("SIGTERM");
7320
+ }
7321
+ };
7322
+ process.on("SIGINT", cleanup);
7323
+ process.on("SIGTERM", cleanup);
7324
+ try {
7325
+ console.log("[SmokeTest] Spawning server process...");
7326
+ server2 = (0, import_child_process2.spawn)("node", [serverPath], {
7327
+ env: {
7328
+ ...process.env,
7329
+ PORT: String(SMOKE_TEST_PORT),
7330
+ NODE_ENV: "test"
7331
+ },
7332
+ stdio: ["ignore", "pipe", "pipe"],
7333
+ cwd: process.cwd()
7334
+ });
7335
+ server2.stdout?.on("data", (data) => {
7336
+ const line = data.toString().trim();
7337
+ if (line) {
7338
+ serverLogs.push(line);
7339
+ console.log(`[Server] ${line}`);
7340
+ }
7341
+ });
7342
+ server2.stderr?.on("data", (data) => {
7343
+ const line = data.toString().trim();
7344
+ if (line) {
7345
+ serverLogs.push(`[stderr] ${line}`);
7346
+ console.error(`[Server] ${line}`);
7347
+ }
7348
+ });
7349
+ server2.on("exit", (code) => {
7350
+ serverExited = true;
7351
+ serverExitCode = code;
7352
+ if (code !== null && code !== 0) {
7353
+ console.error(`[SmokeTest] Server exited with code ${code}`);
7354
+ }
7355
+ });
7356
+ server2.on("error", (err) => {
7357
+ console.error(`[SmokeTest] Failed to spawn server: ${err.message}`);
7358
+ });
7359
+ await new Promise((resolve8) => setTimeout(resolve8, 1e3));
7360
+ if (serverExited) {
7361
+ console.error("[SmokeTest] FAILED: Server crashed during startup");
7362
+ console.error(`[SmokeTest] Exit code: ${serverExitCode}`);
7363
+ console.error("[SmokeTest] Server logs:");
7364
+ serverLogs.forEach((log) => console.error(` ${log}`));
7365
+ process.exit(1);
7366
+ }
7367
+ console.log("[SmokeTest] Waiting for server to be ready...");
7368
+ const healthy = await waitForHealth(SMOKE_TEST_PORT);
7369
+ if (!healthy) {
7370
+ if (serverExited) {
7371
+ console.error("[SmokeTest] FAILED: Server crashed during startup");
7372
+ console.error(`[SmokeTest] Exit code: ${serverExitCode}`);
7373
+ } else {
7374
+ console.error("[SmokeTest] FAILED: Server did not become healthy");
7375
+ }
7376
+ console.error("[SmokeTest] Server logs:");
7377
+ serverLogs.forEach((log) => console.error(` ${log}`));
7378
+ cleanup();
7379
+ process.exit(1);
7380
+ }
7381
+ console.log("[SmokeTest] Server is healthy, calling tools/list...");
7382
+ const result = await callToolsList(SMOKE_TEST_PORT);
7383
+ if (!result.success) {
7384
+ console.error(`[SmokeTest] FAILED: ${result.error}`);
7385
+ console.error("[SmokeTest] Server logs:");
7386
+ serverLogs.forEach((log) => console.error(` ${log}`));
7387
+ cleanup();
7388
+ process.exit(1);
7389
+ }
7390
+ const toolCount = result.tools?.length ?? 0;
7391
+ console.log(`[SmokeTest] tools/list returned ${toolCount} tool(s)`);
7392
+ if (toolCount === 0) {
7393
+ console.error("[SmokeTest] FAILED: No tools registered");
7394
+ cleanup();
7395
+ process.exit(1);
7396
+ }
7397
+ const tools = result.tools;
7398
+ tools.forEach((tool) => {
7399
+ console.log(`[SmokeTest] - ${tool.name ?? "unnamed"}`);
7400
+ });
7401
+ console.log("[SmokeTest] PASSED: Server started and tools/list responded successfully");
7402
+ cleanup();
7403
+ process.exit(0);
7404
+ } catch (err) {
7405
+ console.error(
7406
+ `[SmokeTest] FAILED: ${err instanceof Error ? err.message : String(err)}`
7407
+ );
7408
+ console.error("[SmokeTest] Server logs:");
7409
+ serverLogs.forEach((log) => console.error(` ${log}`));
7410
+ cleanup();
7411
+ process.exit(1);
7412
+ }
7413
+ }
7414
+
7179
7415
  // src/cli/index.ts
7180
7416
  var args = process.argv.slice(2);
7181
7417
  function printUsage2() {
@@ -7264,6 +7500,7 @@ COMMANDS
7264
7500
  Building
7265
7501
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
7266
7502
  build Build your integration using skedyul.config.ts
7503
+ smoke-test Validate built server starts and responds to tools/list
7267
7504
 
7268
7505
  Testing & Debugging
7269
7506
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
@@ -7374,6 +7611,10 @@ async function main() {
7374
7611
  await buildCommand(args.slice(1));
7375
7612
  return;
7376
7613
  }
7614
+ if (command === "smoke-test") {
7615
+ await smokeTestCommand(args.slice(1));
7616
+ return;
7617
+ }
7377
7618
  if (command !== "dev") {
7378
7619
  console.error(`Unknown command: ${command}`);
7379
7620
  console.error(`Run 'skedyul --help' for usage information.`);
@@ -7416,6 +7657,9 @@ async function main() {
7416
7657
  case "build":
7417
7658
  await buildCommand(subArgs);
7418
7659
  break;
7660
+ case "smoke-test":
7661
+ await smokeTestCommand(subArgs);
7662
+ break;
7419
7663
  default:
7420
7664
  console.error(`Unknown dev command: ${subCommand}`);
7421
7665
  console.error(`Run 'skedyul dev --help' for usage information.`);
@@ -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 (lockfile is optional)\nCOPY package.json tsconfig.json ./\nCOPY src ./src\n\n# Copy tsup.config.ts if it exists, otherwise generate based on COMPUTE_LAYER\n# BUILD_EXTERNAL is a comma-separated list of additional externals (e.g., \"twilio,stripe\")\nCOPY tsup.config.t[s] ./\nRUN if [ ! -f tsup.config.ts ]; then \\\n BASE_EXT=\"skedyul,zod\"; \\\n if [ \"$COMPUTE_LAYER\" = \"serverless\" ]; then \\\n BASE_EXT=\"skedyul,skedyul/serverless,zod\"; \\\n FORMAT=\"esm\"; \\\n else \\\n BASE_EXT=\"skedyul,skedyul/dedicated,zod\"; \\\n FORMAT=\"cjs\"; \\\n fi; \\\n if [ -n \"$BUILD_EXTERNAL\" ]; then \\\n ALL_EXT=\"$BASE_EXT,$BUILD_EXTERNAL\"; \\\n else \\\n ALL_EXT=\"$BASE_EXT\"; \\\n fi; \\\n EXT_ARRAY=$(echo \"$ALL_EXT\" | sed 's/,/\",\"/g'); \\\n printf 'import{defineConfig}from\"tsup\";export default defineConfig({entry:[\"src/server/mcp_server.ts\"],format:[\"%s\"],target:\"node22\",outDir:\"dist/server\",clean:true,splitting:false,dts:false,external:[\"%s\"]})' \"$FORMAT\" \"$EXT_ARRAY\" > tsup.config.ts; \\\n fi\n\n# Install dependencies (including dev deps for build), compile, then prune\n# Note: Using --no-frozen-lockfile since lockfile may not exist\nRUN pnpm install --no-frozen-lockfile && \\\n pnpm run build && \\\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)\nCMD [\"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 (lockfile is optional)\nCOPY package.json tsconfig.json skedyul.config.ts ./\nCOPY src ./src\n\n# Copy tsup.config.ts if it exists, otherwise generate based on COMPUTE_LAYER\n# BUILD_EXTERNAL is a comma-separated list of additional externals (e.g., \"twilio,stripe\")\nCOPY tsup.config.t[s] ./\nRUN if [ ! -f tsup.config.ts ]; then \\\n BASE_EXT=\"skedyul,zod\"; \\\n if [ \"$COMPUTE_LAYER\" = \"serverless\" ]; then \\\n BASE_EXT=\"skedyul,skedyul/serverless,zod\"; \\\n FORMAT=\"esm\"; \\\n else \\\n BASE_EXT=\"skedyul,skedyul/dedicated,zod\"; \\\n FORMAT=\"cjs\"; \\\n fi; \\\n if [ -n \"$BUILD_EXTERNAL\" ]; then \\\n ALL_EXT=\"$BASE_EXT,$BUILD_EXTERNAL\"; \\\n else \\\n ALL_EXT=\"$BASE_EXT\"; \\\n fi; \\\n EXT_ARRAY=$(echo \"$ALL_EXT\" | sed 's/,/\",\"/g'); \\\n printf 'import{defineConfig}from\"tsup\";export default defineConfig({entry:[\"src/server/mcp_server.ts\"],format:[\"%s\"],target:\"node22\",outDir:\"dist/server\",clean:true,splitting:false,dts:false,external:[\"%s\"]})' \"$FORMAT\" \"$EXT_ARRAY\" > tsup.config.ts; \\\n fi\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# COMPUTE_LAYER env var tells skedyul build which format to use\n# Smoke test runs before pruning since skedyul CLI is a dev dependency\nRUN pnpm install --no-frozen-lockfile && \\\n COMPUTE_LAYER=$COMPUTE_LAYER pnpm run build && \\\n 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)\nCMD [\"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";
package/dist/index.js CHANGED
@@ -4069,7 +4069,7 @@ WORKDIR /app
4069
4069
  RUN corepack enable && corepack prepare pnpm@latest --activate
4070
4070
 
4071
4071
  # Copy package files (lockfile is optional)
4072
- COPY package.json tsconfig.json ./
4072
+ COPY package.json tsconfig.json skedyul.config.ts ./
4073
4073
  COPY src ./src
4074
4074
 
4075
4075
  # Copy tsup.config.ts if it exists, otherwise generate based on COMPUTE_LAYER
@@ -4093,10 +4093,13 @@ RUN if [ ! -f tsup.config.ts ]; then \\
4093
4093
  printf 'import{defineConfig}from"tsup";export default defineConfig({entry:["src/server/mcp_server.ts"],format:["%s"],target:"node22",outDir:"dist/server",clean:true,splitting:false,dts:false,external:["%s"]})' "$FORMAT" "$EXT_ARRAY" > tsup.config.ts; \\
4094
4094
  fi
4095
4095
 
4096
- # Install dependencies (including dev deps for build), compile, then prune
4096
+ # Install dependencies (including dev deps for build), compile, smoke test, then prune
4097
4097
  # Note: Using --no-frozen-lockfile since lockfile may not exist
4098
+ # COMPUTE_LAYER env var tells skedyul build which format to use
4099
+ # Smoke test runs before pruning since skedyul CLI is a dev dependency
4098
4100
  RUN pnpm install --no-frozen-lockfile && \\
4099
- pnpm run build && \\
4101
+ COMPUTE_LAYER=$COMPUTE_LAYER pnpm run build && \\
4102
+ skedyul smoke-test && \\
4100
4103
  pnpm prune --prod && \\
4101
4104
  pnpm store prune && \\
4102
4105
  rm -rf /tmp/* /var/cache/apk/* ~/.npm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "1.1.3",
3
+ "version": "1.1.7",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "./serverless": {
17
17
  "types": "./dist/server.d.ts",
18
- "import": "./dist/serverless/server.js"
18
+ "import": "./dist/serverless/server.mjs"
19
19
  },
20
20
  "./dedicated": {
21
21
  "types": "./dist/server.d.ts",