skedyul 1.2.51 → 1.3.1

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.
@@ -7,6 +7,8 @@ import type { ToolRegistry, WebhookRegistry, ToolMetadata, WebhookMetadata } fro
7
7
  import type { ServerHooks } from '../types/handlers';
8
8
  import type { CoreApiConfig } from '../core/types';
9
9
  import type { EnvSchema, ComputeLayer, ModelDefinition, RelationshipDefinition, ChannelDefinition, WorkflowDefinition, PageDefinition, NavigationConfig, AgentDefinition, SignalDefinition } from './types';
10
+ import type { QueueRegistry, SerializableQueueConfig } from './queue-config';
11
+ export type { QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './queue-config';
10
12
  /**
11
13
  * Install configuration - defines per-install env vars and SHARED models.
12
14
  * This is configured by users during app installation.
@@ -102,6 +104,8 @@ export interface SkedyulConfig {
102
104
  agents?: AgentDefinition[];
103
105
  /** Build configuration for the integration */
104
106
  build?: BuildConfig;
107
+ /** Rate-limit queue definitions for queuedFetch */
108
+ queues?: QueueRegistry;
105
109
  }
106
110
  /**
107
111
  * Serializable config (for database storage).
@@ -120,6 +124,8 @@ export interface SerializableSkedyulConfig {
120
124
  provision?: ProvisionConfig;
121
125
  /** Agent definitions (stored as-is) */
122
126
  agents?: AgentDefinition[];
127
+ /** Rate-limit queue definitions */
128
+ queues?: Record<string, SerializableQueueConfig>;
123
129
  }
124
130
  /**
125
131
  * Define a Skedyul app configuration with full type safety.
@@ -9,7 +9,7 @@
9
9
  * - All definition types extend BaseDefinition
10
10
  */
11
11
  export * from './types';
12
- export type { InstallConfig, ProvisionConfig, BuildConfig, CorsOptions, SkedyulConfig, SerializableSkedyulConfig, } from './app-config';
12
+ export type { InstallConfig, ProvisionConfig, BuildConfig, CorsOptions, SkedyulConfig, SerializableSkedyulConfig, QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './app-config';
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';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Rate-limit queue configuration for skedyul.config queues.
3
+ */
4
+ export type QueueScope = 'provision' | 'install' | 'provision_endpoint' | 'install_endpoint' | 'global';
5
+ /**
6
+ * Serializable queue config (stored in DB / .skedyul/config.json).
7
+ */
8
+ export interface SerializableQueueConfig {
9
+ scope: QueueScope;
10
+ /** Override endpoint handle for provision_endpoint / install_endpoint scopes */
11
+ endpoint?: string;
12
+ /** Max in-flight operations (concurrency cap) */
13
+ maxConcurrent?: number;
14
+ /** Minimum ms between operation starts */
15
+ minTime?: number;
16
+ /** Token bucket: max operations per refresh window */
17
+ reservoir?: number;
18
+ reservoirRefreshAmount?: number;
19
+ reservoirRefreshInterval?: number;
20
+ /** SDK-level retries inside queuedFetch */
21
+ maxRetries?: number;
22
+ retryDelayMs?: number;
23
+ /** Hard timeout for acquire wait + fn execution (ms) */
24
+ timeout?: number;
25
+ }
26
+ /**
27
+ * Full queue config including non-serializable retry predicate.
28
+ */
29
+ export interface QueueConfig extends SerializableQueueConfig {
30
+ /** Predicate for whether a thrown error should trigger requeue */
31
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
32
+ }
33
+ export type QueueRegistry = Record<string, QueueConfig>;
@@ -87,6 +87,16 @@ var CoreApiService = class {
87
87
  };
88
88
  var coreApiService = new CoreApiService();
89
89
 
90
+ // src/ratelimit/config-loader.ts
91
+ var fs = __toESM(require("fs"));
92
+ var path = __toESM(require("path"));
93
+ var cachedRuntimeQueues = null;
94
+ var registeredServerConfig = null;
95
+ function registerQueueConfig(config) {
96
+ registeredServerConfig = config;
97
+ cachedRuntimeQueues = config.queues ?? null;
98
+ }
99
+
90
100
  // src/server/utils/schema.ts
91
101
  var z = __toESM(require("zod"));
92
102
  function normalizeBilling(billing) {
@@ -265,6 +275,14 @@ function runWithConfig(config, fn) {
265
275
  return requestConfigStorage.run(config, fn);
266
276
  }
267
277
 
278
+ // src/ratelimit/context.ts
279
+ var import_async_hooks2 = require("async_hooks");
280
+ var executionContextStorage = new import_async_hooks2.AsyncLocalStorage();
281
+ var activeOperationStorage = new import_async_hooks2.AsyncLocalStorage();
282
+ function runWithRateLimitExecutionContext(ctx, fn) {
283
+ return executionContextStorage.run(ctx, fn);
284
+ }
285
+
268
286
  // src/errors.ts
269
287
  var InstallError = class extends Error {
270
288
  // Optional: which field caused the error
@@ -284,8 +302,8 @@ var AppAuthInvalidError = class extends Error {
284
302
  };
285
303
 
286
304
  // src/server/context-logger.ts
287
- var import_async_hooks2 = require("async_hooks");
288
- var logContextStorage = new import_async_hooks2.AsyncLocalStorage();
305
+ var import_async_hooks3 = require("async_hooks");
306
+ var logContextStorage = new import_async_hooks3.AsyncLocalStorage();
289
307
  function runWithLogContext(context, fn) {
290
308
  return logContextStorage.run(context, fn);
291
309
  }
@@ -498,9 +516,17 @@ function createCallToolHandler(registry, state, onMaxRequests) {
498
516
  baseUrl: toolEnv.SKEDYUL_API_URL ?? "",
499
517
  apiToken: toolEnv.SKEDYUL_API_TOKEN ?? ""
500
518
  };
519
+ const rateLimitContext = {
520
+ app,
521
+ appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
522
+ invocation,
523
+ isProvisionContext: trigger === "provision"
524
+ };
501
525
  const functionResult = await runWithConfig(requestConfig, async () => {
502
- return await runWithLogContext({ invocation }, async () => {
503
- return await fn(inputs, executionContext);
526
+ return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
527
+ return await runWithLogContext({ invocation }, async () => {
528
+ return await fn(inputs, executionContext);
529
+ });
504
530
  });
505
531
  });
506
532
  const billing = normalizeBilling(functionResult.billing);
@@ -544,6 +570,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
544
570
  };
545
571
  }
546
572
  const errorMessage = error instanceof Error ? error.message : String(error ?? "");
573
+ const errorCode = error && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : "TOOL_EXECUTION_ERROR";
547
574
  return {
548
575
  output: null,
549
576
  billing: { credits: 0 },
@@ -553,7 +580,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
553
580
  toolName
554
581
  },
555
582
  error: {
556
- code: "TOOL_EXECUTION_ERROR",
583
+ code: errorCode,
557
584
  message: errorMessage
558
585
  }
559
586
  };
@@ -637,7 +664,7 @@ function printStartupLog(config, tools, port) {
637
664
 
638
665
  // src/server/route-handlers/adapters.ts
639
666
  function fromLambdaEvent(event) {
640
- const path2 = event.path || event.rawPath || "/";
667
+ const path3 = event.path || event.rawPath || "/";
641
668
  const method = event.httpMethod || event.requestContext?.http?.method || "POST";
642
669
  const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
643
670
  const protocol = forwardedProto ?? "https";
@@ -645,9 +672,9 @@ function fromLambdaEvent(event) {
645
672
  const queryString = event.queryStringParameters ? "?" + new URLSearchParams(
646
673
  event.queryStringParameters
647
674
  ).toString() : "";
648
- const url = `${protocol}://${host}${path2}${queryString}`;
675
+ const url = `${protocol}://${host}${path3}${queryString}`;
649
676
  return {
650
- path: path2,
677
+ path: path3,
651
678
  method,
652
679
  headers: event.headers,
653
680
  query: event.queryStringParameters ?? {},
@@ -729,8 +756,8 @@ function parseBodyByContentType(req) {
729
756
  }
730
757
 
731
758
  // src/server/route-handlers/handlers.ts
732
- var fs = __toESM(require("fs"));
733
- var path = __toESM(require("path"));
759
+ var fs2 = __toESM(require("fs"));
760
+ var path2 = __toESM(require("path"));
734
761
 
735
762
  // src/server/core-api-handler.ts
736
763
  async function handleCoreMethod(method, params) {
@@ -898,7 +925,8 @@ function serializeConfig(config) {
898
925
  type: w.type ?? "WEBHOOK"
899
926
  })) : [],
900
927
  provision: isProvisionConfig(config.provision) ? config.provision : void 0,
901
- agents: config.agents
928
+ agents: config.agents,
929
+ queues: config.queues
902
930
  };
903
931
  }
904
932
  function isProvisionConfig(value) {
@@ -1194,7 +1222,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
1194
1222
  }
1195
1223
 
1196
1224
  // src/server/handlers/webhook-handler.ts
1197
- function parseWebhookRequest(parsedBody, method, url, path2, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1225
+ function parseWebhookRequest(parsedBody, method, url, path3, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1198
1226
  const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
1199
1227
  if (isEnvelope) {
1200
1228
  const envelope = parsedBody;
@@ -1248,7 +1276,7 @@ function parseWebhookRequest(parsedBody, method, url, path2, headers, query, raw
1248
1276
  const webhookRequest = {
1249
1277
  method,
1250
1278
  url,
1251
- path: path2,
1279
+ path: path3,
1252
1280
  headers,
1253
1281
  query,
1254
1282
  body: parsedBody,
@@ -1306,7 +1334,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
1306
1334
 
1307
1335
  // src/server/route-handlers/handlers.ts
1308
1336
  function getConfigFilePath() {
1309
- return process.env.LAMBDA_TASK_ROOT ? path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1337
+ return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1310
1338
  }
1311
1339
  function findToolInRegistry(registry, toolName) {
1312
1340
  for (const [key, t] of Object.entries(registry)) {
@@ -1345,8 +1373,8 @@ function handleHealthRoute(ctx) {
1345
1373
  function handleConfigRoute(ctx) {
1346
1374
  const configFilePath = getConfigFilePath();
1347
1375
  try {
1348
- if (fs.existsSync(configFilePath)) {
1349
- const fileConfig = JSON.parse(fs.readFileSync(configFilePath, "utf-8"));
1376
+ if (fs2.existsSync(configFilePath)) {
1377
+ const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
1350
1378
  return { status: 200, body: fileConfig };
1351
1379
  }
1352
1380
  } catch (err) {
@@ -2031,6 +2059,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
2031
2059
  installContextLogger();
2032
2060
  function createSkedyulServer(config) {
2033
2061
  mergeRuntimeEnv();
2062
+ registerQueueConfig(config);
2034
2063
  const registry = config.tools;
2035
2064
  const webhookRegistry = config.webhooks;
2036
2065
  if (config.coreApi?.service) {