skedyul 1.2.50 → 1.3.0

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);
@@ -637,7 +663,7 @@ function printStartupLog(config, tools, port) {
637
663
 
638
664
  // src/server/route-handlers/adapters.ts
639
665
  function fromLambdaEvent(event) {
640
- const path2 = event.path || event.rawPath || "/";
666
+ const path3 = event.path || event.rawPath || "/";
641
667
  const method = event.httpMethod || event.requestContext?.http?.method || "POST";
642
668
  const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
643
669
  const protocol = forwardedProto ?? "https";
@@ -645,9 +671,9 @@ function fromLambdaEvent(event) {
645
671
  const queryString = event.queryStringParameters ? "?" + new URLSearchParams(
646
672
  event.queryStringParameters
647
673
  ).toString() : "";
648
- const url = `${protocol}://${host}${path2}${queryString}`;
674
+ const url = `${protocol}://${host}${path3}${queryString}`;
649
675
  return {
650
- path: path2,
676
+ path: path3,
651
677
  method,
652
678
  headers: event.headers,
653
679
  query: event.queryStringParameters ?? {},
@@ -729,8 +755,8 @@ function parseBodyByContentType(req) {
729
755
  }
730
756
 
731
757
  // src/server/route-handlers/handlers.ts
732
- var fs = __toESM(require("fs"));
733
- var path = __toESM(require("path"));
758
+ var fs2 = __toESM(require("fs"));
759
+ var path2 = __toESM(require("path"));
734
760
 
735
761
  // src/server/core-api-handler.ts
736
762
  async function handleCoreMethod(method, params) {
@@ -898,7 +924,8 @@ function serializeConfig(config) {
898
924
  type: w.type ?? "WEBHOOK"
899
925
  })) : [],
900
926
  provision: isProvisionConfig(config.provision) ? config.provision : void 0,
901
- agents: config.agents
927
+ agents: config.agents,
928
+ queues: config.queues
902
929
  };
903
930
  }
904
931
  function isProvisionConfig(value) {
@@ -1194,7 +1221,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
1194
1221
  }
1195
1222
 
1196
1223
  // src/server/handlers/webhook-handler.ts
1197
- function parseWebhookRequest(parsedBody, method, url, path2, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1224
+ function parseWebhookRequest(parsedBody, method, url, path3, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1198
1225
  const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
1199
1226
  if (isEnvelope) {
1200
1227
  const envelope = parsedBody;
@@ -1248,7 +1275,7 @@ function parseWebhookRequest(parsedBody, method, url, path2, headers, query, raw
1248
1275
  const webhookRequest = {
1249
1276
  method,
1250
1277
  url,
1251
- path: path2,
1278
+ path: path3,
1252
1279
  headers,
1253
1280
  query,
1254
1281
  body: parsedBody,
@@ -1306,7 +1333,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
1306
1333
 
1307
1334
  // src/server/route-handlers/handlers.ts
1308
1335
  function getConfigFilePath() {
1309
- return process.env.LAMBDA_TASK_ROOT ? path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1336
+ return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1310
1337
  }
1311
1338
  function findToolInRegistry(registry, toolName) {
1312
1339
  for (const [key, t] of Object.entries(registry)) {
@@ -1345,8 +1372,8 @@ function handleHealthRoute(ctx) {
1345
1372
  function handleConfigRoute(ctx) {
1346
1373
  const configFilePath = getConfigFilePath();
1347
1374
  try {
1348
- if (fs.existsSync(configFilePath)) {
1349
- const fileConfig = JSON.parse(fs.readFileSync(configFilePath, "utf-8"));
1375
+ if (fs2.existsSync(configFilePath)) {
1376
+ const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
1350
1377
  return { status: 200, body: fileConfig };
1351
1378
  }
1352
1379
  } catch (err) {
@@ -2031,6 +2058,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
2031
2058
  installContextLogger();
2032
2059
  function createSkedyulServer(config) {
2033
2060
  mergeRuntimeEnv();
2061
+ registerQueueConfig(config);
2034
2062
  const registry = config.tools;
2035
2063
  const webhookRegistry = config.webhooks;
2036
2064
  if (config.coreApi?.service) {