skedyul 1.4.4 → 1.4.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.
package/dist/cli/index.js CHANGED
@@ -2904,6 +2904,16 @@ var AppAuthInvalidError = class extends Error {
2904
2904
  }
2905
2905
  };
2906
2906
 
2907
+ // src/ratelimit/errors.ts
2908
+ var RateLimitExceededError = class extends Error {
2909
+ constructor(retryAfterMs, message = "Rate limit exceeded. Please try again later.") {
2910
+ super(message);
2911
+ this.code = "RATE_LIMITED";
2912
+ this.name = "RateLimitExceededError";
2913
+ this.retryAfterMs = retryAfterMs;
2914
+ }
2915
+ };
2916
+
2907
2917
  // src/server/context-logger.ts
2908
2918
  var import_async_hooks3 = require("async_hooks");
2909
2919
  var logContextStorage = new import_async_hooks3.AsyncLocalStorage();
@@ -2981,6 +2991,7 @@ function buildToolMetadata(registry) {
2981
2991
  const rawRetries = tool.retries ?? toolConfig.retries;
2982
2992
  const timeout = typeof rawTimeout === "number" && rawTimeout > 0 ? rawTimeout : 1e4;
2983
2993
  const retries = typeof rawRetries === "number" && rawRetries >= 1 ? rawRetries : 1;
2994
+ const queueTouchPoints = tool.queueTouchPoints ?? toolConfig.queueTouchPoints;
2984
2995
  return {
2985
2996
  name: tool.name,
2986
2997
  displayName: tool.label || tool.name,
@@ -2990,10 +3001,12 @@ function buildToolMetadata(registry) {
2990
3001
  // Include timeout/retries at top-level for tools/list response (used by syncExecutableTools)
2991
3002
  timeout,
2992
3003
  retries,
3004
+ queueTouchPoints,
2993
3005
  config: {
2994
3006
  timeout,
2995
3007
  retries,
2996
- completionHints: toolConfig.completionHints
3008
+ completionHints: toolConfig.completionHints,
3009
+ queueTouchPoints
2997
3010
  }
2998
3011
  };
2999
3012
  });
@@ -3136,6 +3149,27 @@ function createCallToolHandler(registry, state, onMaxRequests) {
3136
3149
  cursor: functionResult.cursor
3137
3150
  };
3138
3151
  } catch (error) {
3152
+ if (error instanceof RateLimitExceededError) {
3153
+ return {
3154
+ success: false,
3155
+ output: null,
3156
+ billing: { credits: 0 },
3157
+ meta: {
3158
+ success: false,
3159
+ message: error.message,
3160
+ toolName
3161
+ },
3162
+ error: {
3163
+ code: "RATE_LIMITED",
3164
+ message: error.message,
3165
+ category: "external"
3166
+ },
3167
+ retry: {
3168
+ allowed: true,
3169
+ afterMs: error.retryAfterMs
3170
+ }
3171
+ };
3172
+ }
3139
3173
  if (error instanceof AppAuthInvalidError) {
3140
3174
  return {
3141
3175
  output: null,
@@ -3499,7 +3533,8 @@ function serializeConfig(config) {
3499
3533
  description: tool.description,
3500
3534
  // Read timeout/retries from top-level first, then fallback to config
3501
3535
  timeout: tool.timeout ?? tool.config?.timeout,
3502
- retries: tool.retries ?? tool.config?.retries
3536
+ retries: tool.retries ?? tool.config?.retries,
3537
+ queueTouchPoints: tool.queueTouchPoints ?? tool.config?.queueTouchPoints
3503
3538
  })) : [],
3504
3539
  webhooks: webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
3505
3540
  name: w.name,
@@ -3509,6 +3544,7 @@ function serializeConfig(config) {
3509
3544
  })) : [],
3510
3545
  provision: isProvisionConfig(config.provision) ? config.provision : void 0,
3511
3546
  agents: config.agents,
3547
+ events: config.events,
3512
3548
  queues: config.queues
3513
3549
  };
3514
3550
  }
@@ -5823,17 +5859,37 @@ var CONFIG_FILE_NAMES = [
5823
5859
  "skedyul.config.mjs",
5824
5860
  "skedyul.config.cjs"
5825
5861
  ];
5862
+ function loadTypeScriptConfigModule(absolutePath) {
5863
+ try {
5864
+ require("tsx/cjs");
5865
+ delete require.cache[absolutePath];
5866
+ const module2 = require(absolutePath);
5867
+ return module2.default ?? module2;
5868
+ } catch (error) {
5869
+ throw new Error(
5870
+ `Cannot load TypeScript config: ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`
5871
+ );
5872
+ }
5873
+ }
5826
5874
  async function transpileTypeScript(filePath) {
5827
5875
  const content = fs10.readFileSync(filePath, "utf-8");
5828
5876
  const configDir = path10.dirname(path10.resolve(filePath));
5829
5877
  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*$/, "}");
5830
5878
  transpiled = transpiled.replace(
5831
5879
  /import\s+(\w+)\s+from\s+['"](\.[^'"]+)['"]\s*(?:with\s*\{[^}]*\})?/g,
5832
- (match, varName, relativePath) => {
5880
+ (_match, varName, relativePath) => {
5833
5881
  const absolutePath = path10.resolve(configDir, relativePath);
5834
5882
  return `const ${varName} = require('${absolutePath.replace(/\\/g, "/")}')`;
5835
5883
  }
5836
5884
  );
5885
+ transpiled = transpiled.replace(
5886
+ /import\s+\{\s*([^}]+)\s*\}\s+from\s+['"](\.[^'"]+)['"]\s*;?\n?/g,
5887
+ (_match, namedImports, relativePath) => {
5888
+ const absolutePath = path10.resolve(configDir, relativePath);
5889
+ return `const { ${namedImports.trim()} } = require('${absolutePath.replace(/\\/g, "/")}')
5890
+ `;
5891
+ }
5892
+ );
5837
5893
  transpiled = transpiled.replace(/import\s*\(\s*['"][^'"]+['"]\s*\)/g, "null");
5838
5894
  return transpiled;
5839
5895
  }
@@ -5844,16 +5900,9 @@ async function loadConfig(configPath) {
5844
5900
  }
5845
5901
  const isTypeScript = absolutePath.endsWith(".ts");
5846
5902
  try {
5847
- let moduleToLoad = absolutePath;
5848
5903
  if (isTypeScript) {
5849
- const transpiled = await transpileTypeScript(absolutePath);
5850
- const tempDir = os2.tmpdir();
5851
- const tempFile = path10.join(tempDir, `skedyul-config-${Date.now()}.cjs`);
5852
- fs10.writeFileSync(tempFile, transpiled);
5853
- moduleToLoad = tempFile;
5854
5904
  try {
5855
- const module3 = require(moduleToLoad);
5856
- const config2 = module3.default || module3;
5905
+ const config2 = loadTypeScriptConfigModule(absolutePath);
5857
5906
  if (!config2 || typeof config2 !== "object") {
5858
5907
  throw new Error("Config file must export a configuration object");
5859
5908
  }
@@ -5861,16 +5910,31 @@ async function loadConfig(configPath) {
5861
5910
  throw new Error('Config must have a "name" property');
5862
5911
  }
5863
5912
  return config2;
5864
- } finally {
5913
+ } catch (tsxError) {
5914
+ const transpiled = await transpileTypeScript(absolutePath);
5915
+ const tempFile = path10.join(os2.tmpdir(), `skedyul-config-${Date.now()}.cjs`);
5916
+ fs10.writeFileSync(tempFile, transpiled);
5865
5917
  try {
5866
- fs10.unlinkSync(tempFile);
5867
- } catch {
5918
+ const module3 = require(tempFile);
5919
+ const config2 = module3.default || module3;
5920
+ if (!config2 || typeof config2 !== "object") {
5921
+ throw new Error("Config file must export a configuration object");
5922
+ }
5923
+ if (!config2.name || typeof config2.name !== "string") {
5924
+ throw new Error('Config must have a "name" property');
5925
+ }
5926
+ return config2;
5927
+ } finally {
5928
+ try {
5929
+ fs10.unlinkSync(tempFile);
5930
+ } catch {
5931
+ }
5868
5932
  }
5869
5933
  }
5870
5934
  }
5871
5935
  const module2 = await import(
5872
5936
  /* webpackIgnore: true */
5873
- moduleToLoad
5937
+ absolutePath
5874
5938
  );
5875
5939
  const config = module2.default || module2;
5876
5940
  if (!config || typeof config !== "object") {
@@ -6370,6 +6434,7 @@ function serializeResolvedConfig(config) {
6370
6434
  })) : [],
6371
6435
  provision: config.provision,
6372
6436
  agents: config.agents,
6437
+ events: config.events,
6373
6438
  queues: config.queues
6374
6439
  };
6375
6440
  }
@@ -9622,7 +9687,9 @@ var ThreadEventTypeSchema = import_v44.z.enum([
9622
9687
  "thread.workflow.completed",
9623
9688
  // Scheduled events
9624
9689
  "thread.follow_up.due",
9625
- "thread.reminder.due"
9690
+ "thread.reminder.due",
9691
+ // Signal events
9692
+ "thread.signal.created"
9626
9693
  ]);
9627
9694
  var CustomEventTypeSchema = import_v44.z.string().regex(/^custom\.[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/, {
9628
9695
  message: "Custom event type must match pattern: custom.{namespace}.{event}"
@@ -7,6 +7,7 @@ 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 { AppEventDefinition } from './types/app-event';
10
11
  import type { QueueRegistry, SerializableQueueConfig } from './queue-config';
11
12
  export type { QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './queue-config';
12
13
  /**
@@ -102,6 +103,8 @@ export interface SkedyulConfig {
102
103
  }>;
103
104
  /** Agent definitions - multi-tenant agents with tool bindings */
104
105
  agents?: AgentDefinition[];
106
+ /** App events emitted via event.create (catalog for UI + docs) */
107
+ events?: AppEventDefinition[];
105
108
  /** Build configuration for the integration */
106
109
  build?: BuildConfig;
107
110
  /** Rate-limit queue definitions for queuedFetch */
@@ -124,6 +127,8 @@ export interface SerializableSkedyulConfig {
124
127
  provision?: ProvisionConfig;
125
128
  /** Agent definitions (stored as-is) */
126
129
  agents?: AgentDefinition[];
130
+ /** App event catalog */
131
+ events?: AppEventDefinition[];
127
132
  /** Rate-limit queue definitions */
128
133
  queues?: Record<string, SerializableQueueConfig>;
129
134
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * App event definition for integration executable config.
3
+ *
4
+ * Events are emitted via event.create and subscribed to as
5
+ * `app.{appHandle}.{name}` (e.g. app.bft.member.updated).
6
+ */
7
+ export interface AppEventDefinition {
8
+ /** Event suffix after app handle, e.g. member.created */
9
+ name: string;
10
+ /** UI display label */
11
+ label: string;
12
+ /** Optional description for pickers and docs */
13
+ description?: string;
14
+ /** Optional grouping label (Members, Bookings, etc.) */
15
+ group?: string;
16
+ /** Optional Lucide icon name for pickers */
17
+ icon?: string;
18
+ /**
19
+ * Example flat domain payload for liquid context reference (e.g. glofox_id, phone, …).
20
+ * Not the full emit payload — studio/branch metadata is added at emit time.
21
+ */
22
+ examplePayload?: Record<string, unknown>;
23
+ /**
24
+ * Typed context field tree for liquid input path browsing (data.* paths).
25
+ * When set, subscribers see explicit fields like data.phone.
26
+ */
27
+ contextFields?: AppEventContextField[];
28
+ /**
29
+ * Workflow input type for app-event payloads, e.g. @app/bft/member/updated.
30
+ * Workflows declare this on inputs.data and subscribe with {{ data }}.
31
+ */
32
+ workflowInputType?: string;
33
+ }
34
+ export interface AppEventContextField {
35
+ path: string;
36
+ label: string;
37
+ type?: string;
38
+ description?: string;
39
+ children?: AppEventContextField[];
40
+ }
@@ -12,6 +12,7 @@ export * from './workflow';
12
12
  export * from './agent';
13
13
  export * from './webhook';
14
14
  export * from './signal';
15
+ export * from './app-event';
15
16
  export * from './navigation';
16
17
  export * from './context';
17
18
  export * from './form';
@@ -307,6 +307,16 @@ var AppAuthInvalidError = class extends Error {
307
307
  }
308
308
  };
309
309
 
310
+ // src/ratelimit/errors.ts
311
+ var RateLimitExceededError = class extends Error {
312
+ constructor(retryAfterMs, message = "Rate limit exceeded. Please try again later.") {
313
+ super(message);
314
+ this.code = "RATE_LIMITED";
315
+ this.name = "RateLimitExceededError";
316
+ this.retryAfterMs = retryAfterMs;
317
+ }
318
+ };
319
+
310
320
  // src/server/context-logger.ts
311
321
  var import_async_hooks3 = require("async_hooks");
312
322
  var logContextStorage = new import_async_hooks3.AsyncLocalStorage();
@@ -404,6 +414,7 @@ function buildToolMetadata(registry) {
404
414
  const rawRetries = tool.retries ?? toolConfig.retries;
405
415
  const timeout = typeof rawTimeout === "number" && rawTimeout > 0 ? rawTimeout : 1e4;
406
416
  const retries = typeof rawRetries === "number" && rawRetries >= 1 ? rawRetries : 1;
417
+ const queueTouchPoints = tool.queueTouchPoints ?? toolConfig.queueTouchPoints;
407
418
  return {
408
419
  name: tool.name,
409
420
  displayName: tool.label || tool.name,
@@ -413,10 +424,12 @@ function buildToolMetadata(registry) {
413
424
  // Include timeout/retries at top-level for tools/list response (used by syncExecutableTools)
414
425
  timeout,
415
426
  retries,
427
+ queueTouchPoints,
416
428
  config: {
417
429
  timeout,
418
430
  retries,
419
- completionHints: toolConfig.completionHints
431
+ completionHints: toolConfig.completionHints,
432
+ queueTouchPoints
420
433
  }
421
434
  };
422
435
  });
@@ -559,6 +572,27 @@ function createCallToolHandler(registry, state, onMaxRequests) {
559
572
  cursor: functionResult.cursor
560
573
  };
561
574
  } catch (error) {
575
+ if (error instanceof RateLimitExceededError) {
576
+ return {
577
+ success: false,
578
+ output: null,
579
+ billing: { credits: 0 },
580
+ meta: {
581
+ success: false,
582
+ message: error.message,
583
+ toolName
584
+ },
585
+ error: {
586
+ code: "RATE_LIMITED",
587
+ message: error.message,
588
+ category: "external"
589
+ },
590
+ retry: {
591
+ allowed: true,
592
+ afterMs: error.retryAfterMs
593
+ }
594
+ };
595
+ }
562
596
  if (error instanceof AppAuthInvalidError) {
563
597
  return {
564
598
  output: null,
@@ -922,7 +956,8 @@ function serializeConfig(config) {
922
956
  description: tool.description,
923
957
  // Read timeout/retries from top-level first, then fallback to config
924
958
  timeout: tool.timeout ?? tool.config?.timeout,
925
- retries: tool.retries ?? tool.config?.retries
959
+ retries: tool.retries ?? tool.config?.retries,
960
+ queueTouchPoints: tool.queueTouchPoints ?? tool.config?.queueTouchPoints
926
961
  })) : [],
927
962
  webhooks: webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
928
963
  name: w.name,
@@ -932,6 +967,7 @@ function serializeConfig(config) {
932
967
  })) : [],
933
968
  provision: isProvisionConfig(config.provision) ? config.provision : void 0,
934
969
  agents: config.agents,
970
+ events: config.events,
935
971
  queues: config.queues
936
972
  };
937
973
  }