skedyul 1.5.13 → 1.6.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.
- package/dist/cli/index.js +178 -148
- package/dist/config/app-config.d.ts +6 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/sequencer-config.d.ts +20 -0
- package/dist/config/types/model.d.ts +2 -0
- package/dist/core/client.d.ts +23 -8
- package/dist/dedicated/server.js +35 -11
- package/dist/esm/index.mjs +527 -42
- package/dist/index.d.ts +3 -1
- package/dist/index.js +544 -42
- package/dist/schemas/crm-schema.d.ts +10 -0
- package/dist/schemas.d.ts +44 -0
- package/dist/sequencer/backends/index.d.ts +10 -0
- package/dist/sequencer/backends/memory.d.ts +26 -0
- package/dist/sequencer/backends/platform.d.ts +22 -0
- package/dist/sequencer/config-loader.d.ts +9 -0
- package/dist/sequencer/errors.d.ts +17 -0
- package/dist/sequencer/index.d.ts +24 -0
- package/dist/sequencer/resolve-sequencer-key.d.ts +7 -0
- package/dist/sequencer/types.d.ts +50 -0
- package/dist/server.js +35 -11
- package/dist/serverless/server.mjs +35 -11
- package/package.json +2 -2
package/dist/esm/index.mjs
CHANGED
|
@@ -410,6 +410,8 @@ var CRMPageSchemaZ = z3.object({
|
|
|
410
410
|
icon: z3.string().optional(),
|
|
411
411
|
modelHandle: z3.string(),
|
|
412
412
|
parentPath: z3.string().optional(),
|
|
413
|
+
isDefault: z3.boolean().optional(),
|
|
414
|
+
visibility: z3.enum(["team", "private"]).optional(),
|
|
413
415
|
baseQuery: z3.unknown().optional(),
|
|
414
416
|
blocks: z3.array(CRMBlockSchemaZ).optional()
|
|
415
417
|
});
|
|
@@ -1285,7 +1287,8 @@ var ModelDefinitionSchema = z8.object({
|
|
|
1285
1287
|
addDefaultPages: z8.boolean().optional(),
|
|
1286
1288
|
addNavigation: z8.boolean().optional(),
|
|
1287
1289
|
/** Root path for developer resource UI (e.g., '/access_requests'). Links internal model to a provision.pages entry. */
|
|
1288
|
-
page: z8.string().optional()
|
|
1290
|
+
page: z8.string().optional(),
|
|
1291
|
+
env: EnvSchemaSchema.optional()
|
|
1289
1292
|
});
|
|
1290
1293
|
var RelationshipCardinalitySchema = z8.enum([
|
|
1291
1294
|
"ONE_TO_ONE",
|
|
@@ -2097,6 +2100,56 @@ function getQueueConfigWithRetry(queueName) {
|
|
|
2097
2100
|
return serializable;
|
|
2098
2101
|
}
|
|
2099
2102
|
|
|
2103
|
+
// src/sequencer/config-loader.ts
|
|
2104
|
+
import * as fs2 from "fs";
|
|
2105
|
+
import * as path2 from "path";
|
|
2106
|
+
var cachedRuntimeSequencers = null;
|
|
2107
|
+
function getRuntimeConfigPath2() {
|
|
2108
|
+
if (process.env.LAMBDA_TASK_ROOT) {
|
|
2109
|
+
return path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json");
|
|
2110
|
+
}
|
|
2111
|
+
return path2.join(process.cwd(), ".skedyul", "config.json");
|
|
2112
|
+
}
|
|
2113
|
+
function loadRuntimeConfigFile2() {
|
|
2114
|
+
const configPath = getRuntimeConfigPath2();
|
|
2115
|
+
if (!fs2.existsSync(configPath)) {
|
|
2116
|
+
return null;
|
|
2117
|
+
}
|
|
2118
|
+
try {
|
|
2119
|
+
const raw = fs2.readFileSync(configPath, "utf-8");
|
|
2120
|
+
return JSON.parse(raw);
|
|
2121
|
+
} catch {
|
|
2122
|
+
return null;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
var registeredServerConfig2 = null;
|
|
2126
|
+
function registerSequencerConfig(config) {
|
|
2127
|
+
registeredServerConfig2 = config;
|
|
2128
|
+
cachedRuntimeSequencers = config.sequencers ?? null;
|
|
2129
|
+
}
|
|
2130
|
+
function clearRegisteredSequencerConfig() {
|
|
2131
|
+
registeredServerConfig2 = null;
|
|
2132
|
+
cachedRuntimeSequencers = null;
|
|
2133
|
+
}
|
|
2134
|
+
function getSequencerDefinitions() {
|
|
2135
|
+
if (cachedRuntimeSequencers) {
|
|
2136
|
+
return cachedRuntimeSequencers;
|
|
2137
|
+
}
|
|
2138
|
+
if (registeredServerConfig2?.sequencers) {
|
|
2139
|
+
cachedRuntimeSequencers = registeredServerConfig2.sequencers;
|
|
2140
|
+
return cachedRuntimeSequencers;
|
|
2141
|
+
}
|
|
2142
|
+
const fileConfig = loadRuntimeConfigFile2();
|
|
2143
|
+
if (fileConfig?.sequencers) {
|
|
2144
|
+
cachedRuntimeSequencers = fileConfig.sequencers;
|
|
2145
|
+
return cachedRuntimeSequencers;
|
|
2146
|
+
}
|
|
2147
|
+
return {};
|
|
2148
|
+
}
|
|
2149
|
+
function getSequencerConfig(sequencerName) {
|
|
2150
|
+
return getSequencerDefinitions()[sequencerName];
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2100
2153
|
// src/server/utils/schema.ts
|
|
2101
2154
|
import * as z9 from "zod";
|
|
2102
2155
|
function normalizeBilling(billing) {
|
|
@@ -2503,25 +2556,28 @@ function createInstanceClient(config) {
|
|
|
2503
2556
|
pagination: pagination ?? { page: 1, total: 0, hasMore: false, limit: args?.limit ?? 50 }
|
|
2504
2557
|
};
|
|
2505
2558
|
},
|
|
2506
|
-
async get(modelHandle, id) {
|
|
2559
|
+
async get(modelHandle, id, options) {
|
|
2507
2560
|
const { data } = await callCoreWithConfig("instance.get", {
|
|
2508
2561
|
modelHandle,
|
|
2509
|
-
id
|
|
2562
|
+
id,
|
|
2563
|
+
...options?.includeEnv ? { includeEnv: true } : {}
|
|
2510
2564
|
});
|
|
2511
2565
|
return data;
|
|
2512
2566
|
},
|
|
2513
|
-
async create(modelHandle, data) {
|
|
2567
|
+
async create(modelHandle, data, options) {
|
|
2514
2568
|
const { data: inst } = await callCoreWithConfig("instance.create", {
|
|
2515
2569
|
modelHandle,
|
|
2516
|
-
data
|
|
2570
|
+
data,
|
|
2571
|
+
...options?.env ? { env: options.env } : {}
|
|
2517
2572
|
});
|
|
2518
2573
|
return inst;
|
|
2519
2574
|
},
|
|
2520
|
-
async update(modelHandle, id, data) {
|
|
2575
|
+
async update(modelHandle, id, data, options) {
|
|
2521
2576
|
const { data: inst } = await callCoreWithConfig("instance.update", {
|
|
2522
2577
|
modelHandle,
|
|
2523
2578
|
id,
|
|
2524
|
-
data
|
|
2579
|
+
data,
|
|
2580
|
+
...options?.env ? { env: options.env } : {}
|
|
2525
2581
|
});
|
|
2526
2582
|
return inst;
|
|
2527
2583
|
},
|
|
@@ -2620,10 +2676,11 @@ var instance = {
|
|
|
2620
2676
|
* const record = await instance.get('phone_number', 'ins_abc123')
|
|
2621
2677
|
* ```
|
|
2622
2678
|
*/
|
|
2623
|
-
async get(modelHandle, id) {
|
|
2679
|
+
async get(modelHandle, id, options) {
|
|
2624
2680
|
const { data } = await callCore("instance.get", {
|
|
2625
2681
|
modelHandle,
|
|
2626
|
-
id
|
|
2682
|
+
id,
|
|
2683
|
+
...options?.includeEnv ? { includeEnv: true } : {}
|
|
2627
2684
|
});
|
|
2628
2685
|
return data;
|
|
2629
2686
|
},
|
|
@@ -2640,10 +2697,11 @@ var instance = {
|
|
|
2640
2697
|
* })
|
|
2641
2698
|
* ```
|
|
2642
2699
|
*/
|
|
2643
|
-
async create(modelHandle, data) {
|
|
2700
|
+
async create(modelHandle, data, options) {
|
|
2644
2701
|
const { data: instance2 } = await callCore("instance.create", {
|
|
2645
2702
|
modelHandle,
|
|
2646
|
-
data
|
|
2703
|
+
data,
|
|
2704
|
+
...options?.env ? { env: options.env } : {}
|
|
2647
2705
|
});
|
|
2648
2706
|
return instance2;
|
|
2649
2707
|
},
|
|
@@ -2660,11 +2718,12 @@ var instance = {
|
|
|
2660
2718
|
* })
|
|
2661
2719
|
* ```
|
|
2662
2720
|
*/
|
|
2663
|
-
async update(modelHandle, id, data) {
|
|
2721
|
+
async update(modelHandle, id, data, options) {
|
|
2664
2722
|
const { data: instance2 } = await callCore("instance.update", {
|
|
2665
2723
|
modelHandle,
|
|
2666
2724
|
id,
|
|
2667
|
-
data
|
|
2725
|
+
data,
|
|
2726
|
+
...options?.env ? { env: options.env } : {}
|
|
2668
2727
|
});
|
|
2669
2728
|
return instance2;
|
|
2670
2729
|
},
|
|
@@ -4031,7 +4090,7 @@ function printStartupLog(config, tools, port) {
|
|
|
4031
4090
|
|
|
4032
4091
|
// src/server/route-handlers/adapters.ts
|
|
4033
4092
|
function fromLambdaEvent(event2) {
|
|
4034
|
-
const
|
|
4093
|
+
const path6 = event2.path || event2.rawPath || "/";
|
|
4035
4094
|
const method = event2.httpMethod || event2.requestContext?.http?.method || "POST";
|
|
4036
4095
|
const forwardedProto = event2.headers?.["x-forwarded-proto"] ?? event2.headers?.["X-Forwarded-Proto"];
|
|
4037
4096
|
const protocol = forwardedProto ?? "https";
|
|
@@ -4039,9 +4098,9 @@ function fromLambdaEvent(event2) {
|
|
|
4039
4098
|
const queryString = event2.queryStringParameters ? "?" + new URLSearchParams(
|
|
4040
4099
|
event2.queryStringParameters
|
|
4041
4100
|
).toString() : "";
|
|
4042
|
-
const url = `${protocol}://${host}${
|
|
4101
|
+
const url = `${protocol}://${host}${path6}${queryString}`;
|
|
4043
4102
|
return {
|
|
4044
|
-
path:
|
|
4103
|
+
path: path6,
|
|
4045
4104
|
method,
|
|
4046
4105
|
headers: event2.headers,
|
|
4047
4106
|
query: event2.queryStringParameters ?? {},
|
|
@@ -4123,8 +4182,8 @@ function parseBodyByContentType(req) {
|
|
|
4123
4182
|
}
|
|
4124
4183
|
|
|
4125
4184
|
// src/server/route-handlers/handlers.ts
|
|
4126
|
-
import * as
|
|
4127
|
-
import * as
|
|
4185
|
+
import * as fs3 from "fs";
|
|
4186
|
+
import * as path3 from "path";
|
|
4128
4187
|
|
|
4129
4188
|
// src/server/core-api-handler.ts
|
|
4130
4189
|
async function handleCoreMethod(method, params) {
|
|
@@ -4592,7 +4651,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
|
|
|
4592
4651
|
}
|
|
4593
4652
|
|
|
4594
4653
|
// src/server/handlers/webhook-handler.ts
|
|
4595
|
-
function parseWebhookRequest(parsedBody, method, url,
|
|
4654
|
+
function parseWebhookRequest(parsedBody, method, url, path6, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
|
|
4596
4655
|
const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
|
|
4597
4656
|
if (isEnvelope) {
|
|
4598
4657
|
const envelope = parsedBody;
|
|
@@ -4646,7 +4705,7 @@ function parseWebhookRequest(parsedBody, method, url, path5, headers, query, raw
|
|
|
4646
4705
|
const webhookRequest = {
|
|
4647
4706
|
method,
|
|
4648
4707
|
url,
|
|
4649
|
-
path:
|
|
4708
|
+
path: path6,
|
|
4650
4709
|
headers,
|
|
4651
4710
|
query,
|
|
4652
4711
|
body: parsedBody,
|
|
@@ -4675,9 +4734,17 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
|
|
|
4675
4734
|
};
|
|
4676
4735
|
let webhookResponse;
|
|
4677
4736
|
try {
|
|
4737
|
+
const rateLimitContext = {
|
|
4738
|
+
app: data.webhookContext.app,
|
|
4739
|
+
appInstallationId: isRuntimeWebhookContext(data.webhookContext) ? data.webhookContext.appInstallationId : void 0,
|
|
4740
|
+
invocation: data.invocation,
|
|
4741
|
+
isProvisionContext: !isRuntimeWebhookContext(data.webhookContext)
|
|
4742
|
+
};
|
|
4678
4743
|
webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
|
|
4679
4744
|
return await runWithConfig(requestConfig, async () => {
|
|
4680
|
-
return await
|
|
4745
|
+
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
4746
|
+
return await webhookDef.handler(data.webhookRequest, data.webhookContext);
|
|
4747
|
+
});
|
|
4681
4748
|
});
|
|
4682
4749
|
});
|
|
4683
4750
|
} catch (err) {
|
|
@@ -4704,7 +4771,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
4704
4771
|
|
|
4705
4772
|
// src/server/route-handlers/handlers.ts
|
|
4706
4773
|
function getConfigFilePath() {
|
|
4707
|
-
return process.env.LAMBDA_TASK_ROOT ?
|
|
4774
|
+
return process.env.LAMBDA_TASK_ROOT ? path3.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
4708
4775
|
}
|
|
4709
4776
|
function findToolInRegistry(registry, toolName) {
|
|
4710
4777
|
for (const [key, t] of Object.entries(registry)) {
|
|
@@ -4743,8 +4810,8 @@ function handleHealthRoute(ctx) {
|
|
|
4743
4810
|
function handleConfigRoute(ctx) {
|
|
4744
4811
|
const configFilePath = getConfigFilePath();
|
|
4745
4812
|
try {
|
|
4746
|
-
if (
|
|
4747
|
-
const fileConfig = JSON.parse(
|
|
4813
|
+
if (fs3.existsSync(configFilePath)) {
|
|
4814
|
+
const fileConfig = JSON.parse(fs3.readFileSync(configFilePath, "utf-8"));
|
|
4748
4815
|
return { status: 200, body: fileConfig };
|
|
4749
4816
|
}
|
|
4750
4817
|
} catch (err) {
|
|
@@ -5432,6 +5499,7 @@ installContextLogger();
|
|
|
5432
5499
|
function createSkedyulServer(config) {
|
|
5433
5500
|
mergeRuntimeEnv();
|
|
5434
5501
|
registerQueueConfig(config);
|
|
5502
|
+
registerSequencerConfig(config);
|
|
5435
5503
|
const registry = config.tools;
|
|
5436
5504
|
const webhookRegistry = config.webhooks;
|
|
5437
5505
|
if (config.coreApi?.service) {
|
|
@@ -5700,13 +5768,13 @@ function defineNavigation(navigation) {
|
|
|
5700
5768
|
}
|
|
5701
5769
|
|
|
5702
5770
|
// src/config/schema-loader.ts
|
|
5703
|
-
import * as
|
|
5704
|
-
import * as
|
|
5771
|
+
import * as fs4 from "fs";
|
|
5772
|
+
import * as path4 from "path";
|
|
5705
5773
|
import * as os from "os";
|
|
5706
5774
|
|
|
5707
5775
|
// src/config/resolver.ts
|
|
5708
|
-
import * as
|
|
5709
|
-
import * as
|
|
5776
|
+
import * as fs5 from "fs";
|
|
5777
|
+
import * as path5 from "path";
|
|
5710
5778
|
|
|
5711
5779
|
// src/config/utils.ts
|
|
5712
5780
|
function getAllEnvKeys(config) {
|
|
@@ -5809,7 +5877,7 @@ function getApiToken() {
|
|
|
5809
5877
|
const config = getConfig();
|
|
5810
5878
|
return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
|
|
5811
5879
|
}
|
|
5812
|
-
async function callRateLimitApi(
|
|
5880
|
+
async function callRateLimitApi(path6, body) {
|
|
5813
5881
|
const baseUrl = getApiBaseUrl();
|
|
5814
5882
|
const token2 = getApiToken();
|
|
5815
5883
|
if (!baseUrl || !token2) {
|
|
@@ -5817,7 +5885,7 @@ async function callRateLimitApi(path5, body) {
|
|
|
5817
5885
|
"SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform queue coordination"
|
|
5818
5886
|
);
|
|
5819
5887
|
}
|
|
5820
|
-
const response = await fetch(`${baseUrl}/api/internal/ratelimit/${
|
|
5888
|
+
const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path6}`, {
|
|
5821
5889
|
method: "POST",
|
|
5822
5890
|
headers: {
|
|
5823
5891
|
Authorization: `Bearer ${token2}`,
|
|
@@ -5827,11 +5895,11 @@ async function callRateLimitApi(path5, body) {
|
|
|
5827
5895
|
});
|
|
5828
5896
|
const payload = await response.json().catch(() => null);
|
|
5829
5897
|
if (!response.ok || payload?.success === false) {
|
|
5830
|
-
const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${
|
|
5898
|
+
const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${path6} failed with status ${response.status}`;
|
|
5831
5899
|
throw new RateLimitBackendError(message, response.status);
|
|
5832
5900
|
}
|
|
5833
5901
|
if (!payload?.data) {
|
|
5834
|
-
throw new RateLimitBackendError(`Queue coordination API ${
|
|
5902
|
+
throw new RateLimitBackendError(`Queue coordination API ${path6} returned empty data`);
|
|
5835
5903
|
}
|
|
5836
5904
|
return payload.data;
|
|
5837
5905
|
}
|
|
@@ -6286,6 +6354,406 @@ async function queuedFetchResponse(queueInput, url, init) {
|
|
|
6286
6354
|
return queuedFetch(queueInput, () => fetch(url, init));
|
|
6287
6355
|
}
|
|
6288
6356
|
|
|
6357
|
+
// src/sequencer/errors.ts
|
|
6358
|
+
var SequencerContextError = class extends Error {
|
|
6359
|
+
constructor(message) {
|
|
6360
|
+
super(message);
|
|
6361
|
+
this.code = "SEQUENCER_CONTEXT_ERROR";
|
|
6362
|
+
this.name = "SequencerContextError";
|
|
6363
|
+
}
|
|
6364
|
+
};
|
|
6365
|
+
var SequencerNotFoundError = class extends Error {
|
|
6366
|
+
constructor(name) {
|
|
6367
|
+
super(`Sequencer "${name}" is not defined in skedyul.config`);
|
|
6368
|
+
this.code = "SEQUENCER_NOT_FOUND";
|
|
6369
|
+
this.name = "SequencerNotFoundError";
|
|
6370
|
+
}
|
|
6371
|
+
};
|
|
6372
|
+
var SequencerBackendError = class extends Error {
|
|
6373
|
+
constructor(message, statusCode) {
|
|
6374
|
+
super(message);
|
|
6375
|
+
this.code = "SEQUENCER_BACKEND_ERROR";
|
|
6376
|
+
this.name = "SequencerBackendError";
|
|
6377
|
+
this.statusCode = statusCode;
|
|
6378
|
+
}
|
|
6379
|
+
};
|
|
6380
|
+
var SequencerLockError = class extends Error {
|
|
6381
|
+
constructor(message) {
|
|
6382
|
+
super(message);
|
|
6383
|
+
this.code = "SEQUENCER_LOCK_ERROR";
|
|
6384
|
+
this.name = "SequencerLockError";
|
|
6385
|
+
}
|
|
6386
|
+
};
|
|
6387
|
+
|
|
6388
|
+
// src/sequencer/resolve-sequencer-key.ts
|
|
6389
|
+
function resolveEndpointHandle2(config, invocation) {
|
|
6390
|
+
if (config.endpoint) {
|
|
6391
|
+
return config.endpoint;
|
|
6392
|
+
}
|
|
6393
|
+
if (invocation?.toolHandle) {
|
|
6394
|
+
return invocation.toolHandle;
|
|
6395
|
+
}
|
|
6396
|
+
if (invocation?.serverHookHandle) {
|
|
6397
|
+
return invocation.serverHookHandle;
|
|
6398
|
+
}
|
|
6399
|
+
throw new SequencerContextError(
|
|
6400
|
+
`Cannot resolve endpoint handle for sequencer scope "${config.scope}". Set endpoint in sequencer config or ensure invocation.toolHandle/serverHookHandle is available.`
|
|
6401
|
+
);
|
|
6402
|
+
}
|
|
6403
|
+
function appendSubKey2(base, subKey) {
|
|
6404
|
+
return subKey ? `${base}:${subKey}` : base;
|
|
6405
|
+
}
|
|
6406
|
+
function resolveSequencerKey(sequencerName, config, ctx, subKey) {
|
|
6407
|
+
const { app, appInstallationId, invocation, isProvisionContext: isProvisionContext2 } = ctx;
|
|
6408
|
+
switch (config.scope) {
|
|
6409
|
+
case "provision": {
|
|
6410
|
+
const base = `seq:pv:${app.versionId}:${sequencerName}`;
|
|
6411
|
+
return appendSubKey2(base, subKey);
|
|
6412
|
+
}
|
|
6413
|
+
case "install": {
|
|
6414
|
+
if (!appInstallationId) {
|
|
6415
|
+
throw new SequencerContextError(
|
|
6416
|
+
`Sequencer "${sequencerName}" with scope "install" requires appInstallationId in context`
|
|
6417
|
+
);
|
|
6418
|
+
}
|
|
6419
|
+
const base = `seq:in:${appInstallationId}:${sequencerName}`;
|
|
6420
|
+
return appendSubKey2(base, subKey);
|
|
6421
|
+
}
|
|
6422
|
+
case "provision_endpoint": {
|
|
6423
|
+
if (!isProvisionContext2 && appInstallationId) {
|
|
6424
|
+
throw new SequencerContextError(
|
|
6425
|
+
`Sequencer "${sequencerName}" with scope "provision_endpoint" requires provision context (no install)`
|
|
6426
|
+
);
|
|
6427
|
+
}
|
|
6428
|
+
const endpointHandle = resolveEndpointHandle2(config, invocation);
|
|
6429
|
+
const base = `seq:pep:${app.versionId}:${endpointHandle}:${sequencerName}`;
|
|
6430
|
+
return appendSubKey2(base, subKey);
|
|
6431
|
+
}
|
|
6432
|
+
case "install_endpoint": {
|
|
6433
|
+
if (!appInstallationId) {
|
|
6434
|
+
throw new SequencerContextError(
|
|
6435
|
+
`Sequencer "${sequencerName}" with scope "install_endpoint" requires appInstallationId in context`
|
|
6436
|
+
);
|
|
6437
|
+
}
|
|
6438
|
+
const endpointHandle = resolveEndpointHandle2(config, invocation);
|
|
6439
|
+
const base = `seq:iep:${appInstallationId}:${endpointHandle}:${sequencerName}`;
|
|
6440
|
+
return appendSubKey2(base, subKey);
|
|
6441
|
+
}
|
|
6442
|
+
case "global": {
|
|
6443
|
+
const base = `seq:gl:${app.versionId}:${sequencerName}`;
|
|
6444
|
+
return appendSubKey2(base, subKey);
|
|
6445
|
+
}
|
|
6446
|
+
default: {
|
|
6447
|
+
const _exhaustive = config.scope;
|
|
6448
|
+
throw new SequencerContextError(`Unknown sequencer scope: ${String(_exhaustive)}`);
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
|
|
6453
|
+
// src/sequencer/backends/platform.ts
|
|
6454
|
+
function getApiBaseUrl2() {
|
|
6455
|
+
const config = getConfig();
|
|
6456
|
+
if (config.baseUrl) {
|
|
6457
|
+
return config.baseUrl.replace(/\/+$/, "");
|
|
6458
|
+
}
|
|
6459
|
+
return (process.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_NODE_URL ?? "").replace(/\/+$/, "");
|
|
6460
|
+
}
|
|
6461
|
+
function getApiToken2() {
|
|
6462
|
+
const config = getConfig();
|
|
6463
|
+
return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
|
|
6464
|
+
}
|
|
6465
|
+
async function callSequencerApi(path6, body) {
|
|
6466
|
+
const baseUrl = getApiBaseUrl2();
|
|
6467
|
+
const token2 = getApiToken2();
|
|
6468
|
+
if (!baseUrl || !token2) {
|
|
6469
|
+
throw new SequencerBackendError(
|
|
6470
|
+
"SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform sequencer coordination"
|
|
6471
|
+
);
|
|
6472
|
+
}
|
|
6473
|
+
const response = await fetch(`${baseUrl}/api/internal/sequencer/${path6}`, {
|
|
6474
|
+
method: "POST",
|
|
6475
|
+
headers: {
|
|
6476
|
+
Authorization: `Bearer ${token2}`,
|
|
6477
|
+
"Content-Type": "application/json"
|
|
6478
|
+
},
|
|
6479
|
+
body: JSON.stringify(body)
|
|
6480
|
+
});
|
|
6481
|
+
const payload = await response.json().catch(() => null);
|
|
6482
|
+
if (!response.ok || payload?.success === false) {
|
|
6483
|
+
const message = payload?.errors?.[0]?.message ?? `Sequencer coordination API ${path6} failed with status ${response.status}`;
|
|
6484
|
+
throw new SequencerBackendError(message, response.status);
|
|
6485
|
+
}
|
|
6486
|
+
if (!payload?.data) {
|
|
6487
|
+
throw new SequencerBackendError(`Sequencer coordination API ${path6} returned empty data`);
|
|
6488
|
+
}
|
|
6489
|
+
return payload.data;
|
|
6490
|
+
}
|
|
6491
|
+
var PlatformSequencerBackend = class {
|
|
6492
|
+
async allow(sequencerKey, input) {
|
|
6493
|
+
return callSequencerApi("allow", {
|
|
6494
|
+
sequencerKey,
|
|
6495
|
+
timestamp: input.timestamp,
|
|
6496
|
+
leaseId: input.leaseId,
|
|
6497
|
+
watermarkTtlMs: input.watermarkTtlMs
|
|
6498
|
+
});
|
|
6499
|
+
}
|
|
6500
|
+
async acquire(sequencerKey, input) {
|
|
6501
|
+
return callSequencerApi("acquire", {
|
|
6502
|
+
sequencerKey,
|
|
6503
|
+
leaseId: input.leaseId,
|
|
6504
|
+
timestamp: input.timestamp,
|
|
6505
|
+
lockTtlMs: input.lockTtlMs,
|
|
6506
|
+
watermarkTtlMs: input.watermarkTtlMs
|
|
6507
|
+
});
|
|
6508
|
+
}
|
|
6509
|
+
async release(sequencerKey, leaseId) {
|
|
6510
|
+
await callSequencerApi("release", {
|
|
6511
|
+
sequencerKey,
|
|
6512
|
+
leaseId
|
|
6513
|
+
});
|
|
6514
|
+
}
|
|
6515
|
+
};
|
|
6516
|
+
var platformSequencerBackend = new PlatformSequencerBackend();
|
|
6517
|
+
|
|
6518
|
+
// src/sequencer/backends/memory.ts
|
|
6519
|
+
function generateLeaseId2() {
|
|
6520
|
+
return `mem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
6521
|
+
}
|
|
6522
|
+
var MemorySequencerBackend = class {
|
|
6523
|
+
constructor() {
|
|
6524
|
+
this.state = /* @__PURE__ */ new Map();
|
|
6525
|
+
}
|
|
6526
|
+
getState(sequencerKey) {
|
|
6527
|
+
let state = this.state.get(sequencerKey);
|
|
6528
|
+
if (!state) {
|
|
6529
|
+
state = { watermark: 0, lockLeaseId: null, lockExpiresAt: 0 };
|
|
6530
|
+
this.state.set(sequencerKey, state);
|
|
6531
|
+
}
|
|
6532
|
+
if (state.lockLeaseId && state.lockExpiresAt <= Date.now()) {
|
|
6533
|
+
state.lockLeaseId = null;
|
|
6534
|
+
state.lockExpiresAt = 0;
|
|
6535
|
+
}
|
|
6536
|
+
return state;
|
|
6537
|
+
}
|
|
6538
|
+
async allow(sequencerKey, input) {
|
|
6539
|
+
void input.watermarkTtlMs;
|
|
6540
|
+
const state = this.getState(sequencerKey);
|
|
6541
|
+
if (input.timestamp < state.watermark) {
|
|
6542
|
+
return { allowed: false, reason: "stale", watermark: state.watermark };
|
|
6543
|
+
}
|
|
6544
|
+
if (state.lockLeaseId && input.leaseId && state.lockLeaseId !== input.leaseId) {
|
|
6545
|
+
return { allowed: false, reason: "locked", watermark: state.watermark };
|
|
6546
|
+
}
|
|
6547
|
+
state.watermark = Math.max(state.watermark, input.timestamp);
|
|
6548
|
+
return { allowed: true, reason: "ok", watermark: state.watermark };
|
|
6549
|
+
}
|
|
6550
|
+
async acquire(sequencerKey, input) {
|
|
6551
|
+
void input.watermarkTtlMs;
|
|
6552
|
+
const state = this.getState(sequencerKey);
|
|
6553
|
+
const leaseId = input.leaseId || generateLeaseId2();
|
|
6554
|
+
if (state.lockLeaseId && state.lockLeaseId !== leaseId) {
|
|
6555
|
+
return { acquired: false, reason: "locked" };
|
|
6556
|
+
}
|
|
6557
|
+
state.lockLeaseId = leaseId;
|
|
6558
|
+
state.lockExpiresAt = Date.now() + (input.lockTtlMs ?? 6e4);
|
|
6559
|
+
if (input.timestamp !== void 0) {
|
|
6560
|
+
state.watermark = Math.max(state.watermark, input.timestamp);
|
|
6561
|
+
}
|
|
6562
|
+
return { acquired: true, reason: "ok" };
|
|
6563
|
+
}
|
|
6564
|
+
async release(sequencerKey, leaseId) {
|
|
6565
|
+
const state = this.state.get(sequencerKey);
|
|
6566
|
+
if (!state || state.lockLeaseId !== leaseId) {
|
|
6567
|
+
return;
|
|
6568
|
+
}
|
|
6569
|
+
state.lockLeaseId = null;
|
|
6570
|
+
state.lockExpiresAt = 0;
|
|
6571
|
+
}
|
|
6572
|
+
resetForTests() {
|
|
6573
|
+
this.state.clear();
|
|
6574
|
+
}
|
|
6575
|
+
};
|
|
6576
|
+
var memorySequencerBackend = new MemorySequencerBackend();
|
|
6577
|
+
|
|
6578
|
+
// src/sequencer/backends/index.ts
|
|
6579
|
+
var cachedBackend2 = null;
|
|
6580
|
+
function shouldForceMemoryBackend2() {
|
|
6581
|
+
const flag = process.env.SKEDYUL_SEQUENCER_MEMORY;
|
|
6582
|
+
return flag === "1" || flag === "true";
|
|
6583
|
+
}
|
|
6584
|
+
function getSequencerBackend() {
|
|
6585
|
+
if (cachedBackend2) {
|
|
6586
|
+
return cachedBackend2;
|
|
6587
|
+
}
|
|
6588
|
+
if (shouldForceMemoryBackend2()) {
|
|
6589
|
+
cachedBackend2 = memorySequencerBackend;
|
|
6590
|
+
return cachedBackend2;
|
|
6591
|
+
}
|
|
6592
|
+
cachedBackend2 = createResilientBackend2();
|
|
6593
|
+
return cachedBackend2;
|
|
6594
|
+
}
|
|
6595
|
+
function createResilientBackend2() {
|
|
6596
|
+
return {
|
|
6597
|
+
async allow(sequencerKey, input) {
|
|
6598
|
+
try {
|
|
6599
|
+
return await platformSequencerBackend.allow(sequencerKey, input);
|
|
6600
|
+
} catch (error) {
|
|
6601
|
+
if (shouldFallbackToMemory2(error)) {
|
|
6602
|
+
return memorySequencerBackend.allow(sequencerKey, input);
|
|
6603
|
+
}
|
|
6604
|
+
throw error;
|
|
6605
|
+
}
|
|
6606
|
+
},
|
|
6607
|
+
async acquire(sequencerKey, input) {
|
|
6608
|
+
try {
|
|
6609
|
+
return await platformSequencerBackend.acquire(sequencerKey, input);
|
|
6610
|
+
} catch (error) {
|
|
6611
|
+
if (shouldFallbackToMemory2(error)) {
|
|
6612
|
+
return memorySequencerBackend.acquire(sequencerKey, input);
|
|
6613
|
+
}
|
|
6614
|
+
throw error;
|
|
6615
|
+
}
|
|
6616
|
+
},
|
|
6617
|
+
async release(sequencerKey, leaseId) {
|
|
6618
|
+
if (leaseId.startsWith("mem_")) {
|
|
6619
|
+
return memorySequencerBackend.release(sequencerKey, leaseId);
|
|
6620
|
+
}
|
|
6621
|
+
try {
|
|
6622
|
+
await platformSequencerBackend.release(sequencerKey, leaseId);
|
|
6623
|
+
} catch (error) {
|
|
6624
|
+
if (shouldFallbackToMemory2(error)) {
|
|
6625
|
+
return memorySequencerBackend.release(sequencerKey, leaseId);
|
|
6626
|
+
}
|
|
6627
|
+
throw error;
|
|
6628
|
+
}
|
|
6629
|
+
}
|
|
6630
|
+
};
|
|
6631
|
+
}
|
|
6632
|
+
function shouldFallbackToMemory2(error) {
|
|
6633
|
+
if (shouldForceMemoryBackend2()) {
|
|
6634
|
+
return true;
|
|
6635
|
+
}
|
|
6636
|
+
if (error instanceof SequencerBackendError) {
|
|
6637
|
+
if (error.statusCode === 404 || error.statusCode === 503) {
|
|
6638
|
+
return true;
|
|
6639
|
+
}
|
|
6640
|
+
}
|
|
6641
|
+
if (error instanceof TypeError) {
|
|
6642
|
+
return true;
|
|
6643
|
+
}
|
|
6644
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
6645
|
+
return nodeEnv === "development" || nodeEnv === "test";
|
|
6646
|
+
}
|
|
6647
|
+
function resetSequencerBackendForTests() {
|
|
6648
|
+
cachedBackend2 = null;
|
|
6649
|
+
}
|
|
6650
|
+
|
|
6651
|
+
// src/sequencer/index.ts
|
|
6652
|
+
function normalizeSequencerInput(input) {
|
|
6653
|
+
if (typeof input === "string") {
|
|
6654
|
+
return { name: input };
|
|
6655
|
+
}
|
|
6656
|
+
return { name: input.sequencer, subKey: input.key };
|
|
6657
|
+
}
|
|
6658
|
+
function mergeContext(ctxOverride) {
|
|
6659
|
+
const ctx = getRateLimitExecutionContext();
|
|
6660
|
+
if (!ctx?.app?.versionId) {
|
|
6661
|
+
throw new SequencerContextError(
|
|
6662
|
+
"Cannot resolve sequencer without app context. Ensure sequencer runs inside a tool/hook/webhook handler."
|
|
6663
|
+
);
|
|
6664
|
+
}
|
|
6665
|
+
return { ...ctx, ...ctxOverride };
|
|
6666
|
+
}
|
|
6667
|
+
function resolveSequencer(sequencerInput, ctxOverride) {
|
|
6668
|
+
const ctx = mergeContext(ctxOverride);
|
|
6669
|
+
const { name, subKey } = normalizeSequencerInput(sequencerInput);
|
|
6670
|
+
const config = getSequencerConfig(name);
|
|
6671
|
+
if (!config) {
|
|
6672
|
+
throw new SequencerNotFoundError(name);
|
|
6673
|
+
}
|
|
6674
|
+
const sequencerKey = resolveSequencerKey(name, config, ctx, subKey);
|
|
6675
|
+
return { name, sequencerKey, config };
|
|
6676
|
+
}
|
|
6677
|
+
function createSequencerHandle(sequencerInput, ctxOverride) {
|
|
6678
|
+
return {
|
|
6679
|
+
allow(input) {
|
|
6680
|
+
return allow(sequencerInput, input, ctxOverride);
|
|
6681
|
+
},
|
|
6682
|
+
acquire(input) {
|
|
6683
|
+
return acquire(sequencerInput, input, ctxOverride);
|
|
6684
|
+
},
|
|
6685
|
+
release(input) {
|
|
6686
|
+
return release(sequencerInput, input, ctxOverride);
|
|
6687
|
+
}
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
function disabledAllowResult() {
|
|
6691
|
+
return { allowed: true, reason: "disabled" };
|
|
6692
|
+
}
|
|
6693
|
+
async function allow(sequencerInput, input, ctxOverride) {
|
|
6694
|
+
const { name, subKey } = normalizeSequencerInput(sequencerInput);
|
|
6695
|
+
const config = getSequencerConfig(name);
|
|
6696
|
+
if (!config || config.enabled !== true) {
|
|
6697
|
+
return disabledAllowResult();
|
|
6698
|
+
}
|
|
6699
|
+
const resolvedSubKey = input.key ?? subKey;
|
|
6700
|
+
const resolved = resolveSequencer(
|
|
6701
|
+
{ sequencer: name, key: resolvedSubKey },
|
|
6702
|
+
ctxOverride
|
|
6703
|
+
);
|
|
6704
|
+
const backend = getSequencerBackend();
|
|
6705
|
+
return backend.allow(resolved.sequencerKey, {
|
|
6706
|
+
timestamp: input.timestamp,
|
|
6707
|
+
leaseId: input.leaseId,
|
|
6708
|
+
watermarkTtlMs: resolved.config.watermarkTtlMs
|
|
6709
|
+
});
|
|
6710
|
+
}
|
|
6711
|
+
async function acquire(sequencerInput, input, ctxOverride) {
|
|
6712
|
+
const { name, subKey } = normalizeSequencerInput(sequencerInput);
|
|
6713
|
+
const config = getSequencerConfig(name);
|
|
6714
|
+
if (!config || config.enabled !== true) {
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
const resolvedSubKey = input.key ?? subKey;
|
|
6718
|
+
const resolved = resolveSequencer(
|
|
6719
|
+
{ sequencer: name, key: resolvedSubKey },
|
|
6720
|
+
ctxOverride
|
|
6721
|
+
);
|
|
6722
|
+
const backend = getSequencerBackend();
|
|
6723
|
+
const result = await backend.acquire(resolved.sequencerKey, {
|
|
6724
|
+
leaseId: input.leaseId,
|
|
6725
|
+
timestamp: input.timestamp,
|
|
6726
|
+
lockTtlMs: input.lockTtlMs ?? resolved.config.lockTtlMs,
|
|
6727
|
+
watermarkTtlMs: resolved.config.watermarkTtlMs
|
|
6728
|
+
});
|
|
6729
|
+
if (!result.acquired) {
|
|
6730
|
+
throw new SequencerLockError(
|
|
6731
|
+
`Sequencer "${name}" lock held by another lease (${result.reason})`
|
|
6732
|
+
);
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
async function release(sequencerInput, input, ctxOverride) {
|
|
6736
|
+
const { name, subKey } = normalizeSequencerInput(sequencerInput);
|
|
6737
|
+
const config = getSequencerConfig(name);
|
|
6738
|
+
if (!config || config.enabled !== true) {
|
|
6739
|
+
return;
|
|
6740
|
+
}
|
|
6741
|
+
const resolvedSubKey = input.key ?? subKey;
|
|
6742
|
+
const resolved = resolveSequencer(
|
|
6743
|
+
{ sequencer: name, key: resolvedSubKey },
|
|
6744
|
+
ctxOverride
|
|
6745
|
+
);
|
|
6746
|
+
const backend = getSequencerBackend();
|
|
6747
|
+
await backend.release(resolved.sequencerKey, input.leaseId);
|
|
6748
|
+
}
|
|
6749
|
+
var sequencer = {
|
|
6750
|
+
allow,
|
|
6751
|
+
acquire,
|
|
6752
|
+
release,
|
|
6753
|
+
resolve: resolveSequencer,
|
|
6754
|
+
createHandle: createSequencerHandle
|
|
6755
|
+
};
|
|
6756
|
+
|
|
6289
6757
|
// src/triggers/types.ts
|
|
6290
6758
|
import { z as z12 } from "zod/v4";
|
|
6291
6759
|
var CRMDataSchema = z12.object({
|
|
@@ -6390,16 +6858,16 @@ function evaluateTemplate(template, context) {
|
|
|
6390
6858
|
const templateRegex = /\{\{\s*([^}]+)\s*\}\}/g;
|
|
6391
6859
|
const singleMatch = template.match(/^\{\{\s*([^}]+)\s*\}\}$/);
|
|
6392
6860
|
if (singleMatch) {
|
|
6393
|
-
const
|
|
6394
|
-
return resolvePath(context,
|
|
6861
|
+
const path6 = singleMatch[1].trim();
|
|
6862
|
+
return resolvePath(context, path6);
|
|
6395
6863
|
}
|
|
6396
|
-
return template.replace(templateRegex, (_,
|
|
6397
|
-
const value = resolvePath(context,
|
|
6864
|
+
return template.replace(templateRegex, (_, path6) => {
|
|
6865
|
+
const value = resolvePath(context, path6.trim());
|
|
6398
6866
|
return value === void 0 || value === null ? "" : String(value);
|
|
6399
6867
|
});
|
|
6400
6868
|
}
|
|
6401
|
-
function resolvePath(obj,
|
|
6402
|
-
const parts =
|
|
6869
|
+
function resolvePath(obj, path6) {
|
|
6870
|
+
const parts = path6.split(".");
|
|
6403
6871
|
let current = obj;
|
|
6404
6872
|
for (const part of parts) {
|
|
6405
6873
|
if (current === null || current === void 0) {
|
|
@@ -6420,14 +6888,14 @@ function evaluateCondition(condition, context) {
|
|
|
6420
6888
|
const expression = match[1].trim();
|
|
6421
6889
|
const eqMatch = expression.match(/^(.+?)\s*==\s*['"](.+)['"]$/);
|
|
6422
6890
|
if (eqMatch) {
|
|
6423
|
-
const [,
|
|
6424
|
-
const actualValue = resolvePath(context,
|
|
6891
|
+
const [, path6, expectedValue] = eqMatch;
|
|
6892
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
6425
6893
|
return actualValue === expectedValue;
|
|
6426
6894
|
}
|
|
6427
6895
|
const neqMatch = expression.match(/^(.+?)\s*!=\s*['"](.+)['"]$/);
|
|
6428
6896
|
if (neqMatch) {
|
|
6429
|
-
const [,
|
|
6430
|
-
const actualValue = resolvePath(context,
|
|
6897
|
+
const [, path6, expectedValue] = neqMatch;
|
|
6898
|
+
const actualValue = resolvePath(context, path6.trim());
|
|
6431
6899
|
return actualValue !== expectedValue;
|
|
6432
6900
|
}
|
|
6433
6901
|
const value = resolvePath(context, expression);
|
|
@@ -7879,6 +8347,10 @@ export {
|
|
|
7879
8347
|
SelectComponentDefinitionSchema,
|
|
7880
8348
|
SemanticMemoryConfigSchema2 as SemanticMemoryConfigSchema,
|
|
7881
8349
|
SenderContextSchema,
|
|
8350
|
+
SequencerBackendError,
|
|
8351
|
+
SequencerContextError,
|
|
8352
|
+
SequencerLockError,
|
|
8353
|
+
SequencerNotFoundError,
|
|
7882
8354
|
SkedyulConfigSchema,
|
|
7883
8355
|
SkillExampleSchema,
|
|
7884
8356
|
SkillMetadataSchema,
|
|
@@ -7934,6 +8406,7 @@ export {
|
|
|
7934
8406
|
buildAgentContext,
|
|
7935
8407
|
calculateWaitTime,
|
|
7936
8408
|
call,
|
|
8409
|
+
clearRegisteredSequencerConfig,
|
|
7937
8410
|
communicationChannel,
|
|
7938
8411
|
compileAgent,
|
|
7939
8412
|
compileWorkflow,
|
|
@@ -7953,6 +8426,7 @@ export {
|
|
|
7953
8426
|
createPermissionError,
|
|
7954
8427
|
createQueueHandle,
|
|
7955
8428
|
createRateLimitError,
|
|
8429
|
+
createSequencerHandle,
|
|
7956
8430
|
createServerHookContext,
|
|
7957
8431
|
createSuccessResponse,
|
|
7958
8432
|
createTimeoutError,
|
|
@@ -7991,6 +8465,8 @@ export {
|
|
|
7991
8465
|
getRateLimitExecutionContext,
|
|
7992
8466
|
getRequiredInstallEnvKeys,
|
|
7993
8467
|
getRetryDelay,
|
|
8468
|
+
getSequencerConfig,
|
|
8469
|
+
getSequencerDefinitions,
|
|
7994
8470
|
instance,
|
|
7995
8471
|
isChannelDependency,
|
|
7996
8472
|
isCronContext,
|
|
@@ -8006,20 +8482,29 @@ export {
|
|
|
8006
8482
|
isTimeInWindowSlot2 as isTimeInWindowSlot,
|
|
8007
8483
|
isWorkflowDependency,
|
|
8008
8484
|
matchesTrigger,
|
|
8485
|
+
memorySequencerBackend,
|
|
8009
8486
|
parseCRMSchema,
|
|
8010
8487
|
parseEstimationFromBilling,
|
|
8488
|
+
platformSequencerBackend,
|
|
8011
8489
|
queuedFetch,
|
|
8012
8490
|
queuedFetchResponse,
|
|
8013
8491
|
registerQueueConfig,
|
|
8492
|
+
registerSequencerConfig,
|
|
8014
8493
|
report,
|
|
8015
8494
|
requeue,
|
|
8495
|
+
resetSequencerBackendForTests,
|
|
8016
8496
|
resolveInputMappings,
|
|
8017
8497
|
resolveQueue,
|
|
8498
|
+
resolveSequencer,
|
|
8018
8499
|
resource,
|
|
8019
8500
|
runWithConfig,
|
|
8020
8501
|
runWithRateLimitExecutionContext,
|
|
8021
8502
|
safeParseCRMSchema,
|
|
8022
8503
|
safeParseConfig,
|
|
8504
|
+
sequencer,
|
|
8505
|
+
acquire as sequencerAcquire,
|
|
8506
|
+
allow as sequencerAllow,
|
|
8507
|
+
release as sequencerRelease,
|
|
8023
8508
|
server,
|
|
8024
8509
|
toGsm7,
|
|
8025
8510
|
token,
|