skedyul 1.7.5 → 1.7.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/.build-stamp +1 -1
- package/dist/cli/index.js +108 -0
- package/dist/config/app-config.d.ts +5 -1
- package/dist/config/define.d.ts +34 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/types/batch-operation.d.ts +145 -0
- package/dist/config/types/index.d.ts +1 -0
- package/dist/dedicated/server.js +108 -0
- package/dist/esm/index.mjs +165 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +170 -11
- package/dist/scheduling/calculateWaitTime.d.ts +4 -2
- package/dist/scheduling/index.js +12 -9
- package/dist/scheduling/index.mjs +12 -9
- package/dist/scheduling/types-workflow.d.ts +7 -0
- package/dist/scheduling/types.d.ts +2 -0
- package/dist/schemas.d.ts +53 -0
- package/dist/server/index.d.ts +3 -2
- package/dist/server/route-handlers/handlers.d.ts +4 -0
- package/dist/server/route-handlers/index.d.ts +1 -1
- package/dist/server/route-handlers/types.d.ts +2 -1
- package/dist/server.js +108 -0
- package/dist/serverless/server.mjs +108 -0
- package/dist/types/batch-operation.d.ts +47 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -1901,6 +1901,31 @@ var SetupStepDefinitionSchema = z8.object({
|
|
|
1901
1901
|
listenToEnv: z8.boolean().optional(),
|
|
1902
1902
|
capabilities: z8.array(z8.string()).optional()
|
|
1903
1903
|
});
|
|
1904
|
+
var BatchPaginationSchema = z8.object({
|
|
1905
|
+
hasMore: z8.boolean(),
|
|
1906
|
+
page: z8.number().int().optional(),
|
|
1907
|
+
total: z8.number().int().optional(),
|
|
1908
|
+
nextCursor: z8.union([z8.string(), z8.number()]).optional(),
|
|
1909
|
+
limit: z8.number().int().positive().optional()
|
|
1910
|
+
});
|
|
1911
|
+
var BatchOperationSetupResultSchema = z8.object({
|
|
1912
|
+
state: z8.record(z8.string(), z8.unknown()).optional(),
|
|
1913
|
+
total: z8.number().int().nonnegative().optional()
|
|
1914
|
+
});
|
|
1915
|
+
var BatchOperationIterateResultSchema = z8.object({
|
|
1916
|
+
items: z8.array(z8.record(z8.string(), z8.unknown())),
|
|
1917
|
+
pagination: BatchPaginationSchema,
|
|
1918
|
+
state: z8.record(z8.string(), z8.unknown()).optional()
|
|
1919
|
+
});
|
|
1920
|
+
var BatchOperationDefinitionSchema = z8.object({
|
|
1921
|
+
handle: z8.string(),
|
|
1922
|
+
label: z8.string(),
|
|
1923
|
+
description: z8.string().optional(),
|
|
1924
|
+
entity: z8.string(),
|
|
1925
|
+
maxConcurrent: z8.number().int().positive().optional(),
|
|
1926
|
+
pageSize: z8.number().int().positive().optional(),
|
|
1927
|
+
icon: z8.string().optional()
|
|
1928
|
+
});
|
|
1904
1929
|
var ProvisionConfigSchema = z8.object({
|
|
1905
1930
|
env: EnvSchemaSchema.optional(),
|
|
1906
1931
|
/** INTERNAL model definitions (app-owned, not visible to users) */
|
|
@@ -1915,7 +1940,9 @@ var ProvisionConfigSchema = z8.object({
|
|
|
1915
1940
|
navigation: NavigationConfigSchema.optional(),
|
|
1916
1941
|
pages: z8.array(PageDefinitionSchema).optional(),
|
|
1917
1942
|
/** Install setup checklist steps (opt-in) */
|
|
1918
|
-
setup: z8.array(SetupStepDefinitionSchema).optional()
|
|
1943
|
+
setup: z8.array(SetupStepDefinitionSchema).optional(),
|
|
1944
|
+
/** Batch operations for imports, syncs, and long-running tasks */
|
|
1945
|
+
batchOperations: z8.array(BatchOperationDefinitionSchema).optional()
|
|
1919
1946
|
});
|
|
1920
1947
|
var SkedyulConfigSchema = z8.object({
|
|
1921
1948
|
name: z8.string(),
|
|
@@ -5437,6 +5464,107 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
5437
5464
|
}
|
|
5438
5465
|
};
|
|
5439
5466
|
}
|
|
5467
|
+
async function handleBatchOperationRoute(req, ctx) {
|
|
5468
|
+
const bodyResult = parseJsonBody(req);
|
|
5469
|
+
if (!bodyResult.success) {
|
|
5470
|
+
return bodyResult.error;
|
|
5471
|
+
}
|
|
5472
|
+
const body = bodyResult.data;
|
|
5473
|
+
const registry = ctx.batchOperationRegistry;
|
|
5474
|
+
if (!registry) {
|
|
5475
|
+
return {
|
|
5476
|
+
status: 404,
|
|
5477
|
+
body: {
|
|
5478
|
+
success: false,
|
|
5479
|
+
error: "No batch operations registered"
|
|
5480
|
+
}
|
|
5481
|
+
};
|
|
5482
|
+
}
|
|
5483
|
+
const operation = registry[body.handle];
|
|
5484
|
+
if (!operation) {
|
|
5485
|
+
return {
|
|
5486
|
+
status: 404,
|
|
5487
|
+
body: {
|
|
5488
|
+
success: false,
|
|
5489
|
+
error: `Batch operation not found: ${body.handle}`
|
|
5490
|
+
}
|
|
5491
|
+
};
|
|
5492
|
+
}
|
|
5493
|
+
const log = {
|
|
5494
|
+
info: (message, meta) => {
|
|
5495
|
+
console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5496
|
+
},
|
|
5497
|
+
warn: (message, meta) => {
|
|
5498
|
+
console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5499
|
+
},
|
|
5500
|
+
error: (message, meta) => {
|
|
5501
|
+
console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5502
|
+
}
|
|
5503
|
+
};
|
|
5504
|
+
try {
|
|
5505
|
+
if (body.method === "setup") {
|
|
5506
|
+
if (!operation.setup) {
|
|
5507
|
+
return {
|
|
5508
|
+
status: 200,
|
|
5509
|
+
body: {
|
|
5510
|
+
success: true,
|
|
5511
|
+
result: { state: {}, total: void 0 }
|
|
5512
|
+
}
|
|
5513
|
+
};
|
|
5514
|
+
}
|
|
5515
|
+
const result = await operation.setup({
|
|
5516
|
+
workplaceId: body.workplaceId,
|
|
5517
|
+
appInstallationId: body.appInstallationId,
|
|
5518
|
+
appId: body.appId,
|
|
5519
|
+
input: body.input,
|
|
5520
|
+
log
|
|
5521
|
+
});
|
|
5522
|
+
return {
|
|
5523
|
+
status: 200,
|
|
5524
|
+
body: {
|
|
5525
|
+
success: true,
|
|
5526
|
+
result
|
|
5527
|
+
}
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5530
|
+
if (body.method === "iterate") {
|
|
5531
|
+
const result = await operation.iterate({
|
|
5532
|
+
workplaceId: body.workplaceId,
|
|
5533
|
+
appInstallationId: body.appInstallationId,
|
|
5534
|
+
appId: body.appId,
|
|
5535
|
+
input: body.input,
|
|
5536
|
+
state: body.state,
|
|
5537
|
+
page: body.page,
|
|
5538
|
+
cursor: body.cursor,
|
|
5539
|
+
limit: body.limit,
|
|
5540
|
+
log
|
|
5541
|
+
});
|
|
5542
|
+
return {
|
|
5543
|
+
status: 200,
|
|
5544
|
+
body: {
|
|
5545
|
+
success: true,
|
|
5546
|
+
result
|
|
5547
|
+
}
|
|
5548
|
+
};
|
|
5549
|
+
}
|
|
5550
|
+
return {
|
|
5551
|
+
status: 400,
|
|
5552
|
+
body: {
|
|
5553
|
+
success: false,
|
|
5554
|
+
error: `Unknown method: ${body.method}`
|
|
5555
|
+
}
|
|
5556
|
+
};
|
|
5557
|
+
} catch (err) {
|
|
5558
|
+
log.error("Batch operation failed", { error: err });
|
|
5559
|
+
return {
|
|
5560
|
+
status: 500,
|
|
5561
|
+
body: {
|
|
5562
|
+
success: false,
|
|
5563
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5564
|
+
}
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5440
5568
|
function createNotFoundResponse() {
|
|
5441
5569
|
return {
|
|
5442
5570
|
status: 404,
|
|
@@ -5516,6 +5644,9 @@ async function routeRequest(req, ctx) {
|
|
|
5516
5644
|
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
5517
5645
|
return handleMcpBatchRoute(req, ctx);
|
|
5518
5646
|
}
|
|
5647
|
+
if (req.path === "/batch-operation" && req.method === "POST") {
|
|
5648
|
+
return handleBatchOperationRoute(req, ctx);
|
|
5649
|
+
}
|
|
5519
5650
|
return createNotFoundResponse();
|
|
5520
5651
|
} catch (err) {
|
|
5521
5652
|
return createErrorResponse2(err);
|
|
@@ -5557,11 +5688,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
5557
5688
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
5558
5689
|
const registry = config.tools;
|
|
5559
5690
|
const webhookRegistry = config.webhooks;
|
|
5691
|
+
const batchOperationRegistry = config.batchOperations;
|
|
5560
5692
|
const ctx = {
|
|
5561
5693
|
config,
|
|
5562
5694
|
tools,
|
|
5563
5695
|
registry,
|
|
5564
5696
|
webhookRegistry,
|
|
5697
|
+
batchOperationRegistry,
|
|
5565
5698
|
callTool,
|
|
5566
5699
|
state,
|
|
5567
5700
|
mcpServer,
|
|
@@ -5676,12 +5809,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
5676
5809
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
5677
5810
|
const registry = config.tools;
|
|
5678
5811
|
const webhookRegistry = config.webhooks;
|
|
5812
|
+
const batchOperationRegistry = config.batchOperations;
|
|
5679
5813
|
let hasLoggedStartup = false;
|
|
5680
5814
|
const ctx = {
|
|
5681
5815
|
config,
|
|
5682
5816
|
tools,
|
|
5683
5817
|
registry,
|
|
5684
5818
|
webhookRegistry,
|
|
5819
|
+
batchOperationRegistry,
|
|
5685
5820
|
callTool,
|
|
5686
5821
|
state,
|
|
5687
5822
|
mcpServer,
|
|
@@ -5979,6 +6114,9 @@ function defineNavigation(navigation) {
|
|
|
5979
6114
|
function defineSetupStep(step) {
|
|
5980
6115
|
return step;
|
|
5981
6116
|
}
|
|
6117
|
+
function defineBatchOperation(operation) {
|
|
6118
|
+
return operation;
|
|
6119
|
+
}
|
|
5982
6120
|
|
|
5983
6121
|
// src/config/schema-loader.ts
|
|
5984
6122
|
import * as fs4 from "fs";
|
|
@@ -8027,7 +8165,15 @@ var WaitInputRelativeSchema = z15.object({
|
|
|
8027
8165
|
mode: z15.literal("relative"),
|
|
8028
8166
|
amount: z15.number(),
|
|
8029
8167
|
unit: WaitUnitSchema,
|
|
8030
|
-
windows: z15.array(TimeWindowSlotSchema).optional()
|
|
8168
|
+
windows: z15.array(TimeWindowSlotSchema).optional(),
|
|
8169
|
+
/**
|
|
8170
|
+
* Optional ISO datetime to use as the base for the relative delay instead of
|
|
8171
|
+
* wall-clock `now`. Used for cadence chaining (schedule relative to a prior
|
|
8172
|
+
* message's scheduledAt). `waitTime` is still measured from wall-clock `now`.
|
|
8173
|
+
*/
|
|
8174
|
+
from: z15.union([z15.string(), z15.date()]).optional().describe(
|
|
8175
|
+
"ISO datetime base for the relative delay (e.g. previous message scheduledAt)"
|
|
8176
|
+
)
|
|
8031
8177
|
});
|
|
8032
8178
|
var WaitInputAbsoluteSchema = z15.object({
|
|
8033
8179
|
mode: z15.literal("absolute"),
|
|
@@ -8140,10 +8286,12 @@ function calculateWaitTime(step, now) {
|
|
|
8140
8286
|
};
|
|
8141
8287
|
}
|
|
8142
8288
|
case "relative": {
|
|
8289
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
8290
|
+
const baseDate = new Date(baseTime);
|
|
8143
8291
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
8144
8292
|
return {
|
|
8145
|
-
waitTime: 0,
|
|
8146
|
-
scheduledAt:
|
|
8293
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
8294
|
+
scheduledAt: baseDate
|
|
8147
8295
|
};
|
|
8148
8296
|
}
|
|
8149
8297
|
let relativeDelay = 0;
|
|
@@ -8182,17 +8330,18 @@ function calculateWaitTime(step, now) {
|
|
|
8182
8330
|
}
|
|
8183
8331
|
}
|
|
8184
8332
|
if (!step.windows || step.windows.length === 0) {
|
|
8333
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
8185
8334
|
return {
|
|
8186
|
-
waitTime:
|
|
8187
|
-
scheduledAt
|
|
8335
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
8336
|
+
scheduledAt
|
|
8188
8337
|
};
|
|
8189
8338
|
}
|
|
8190
|
-
const targetDate = new Date(
|
|
8339
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
8191
8340
|
for (const window of step.windows) {
|
|
8192
8341
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
8193
8342
|
return {
|
|
8194
|
-
waitTime:
|
|
8195
|
-
scheduledAt:
|
|
8343
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
8344
|
+
scheduledAt: targetDate
|
|
8196
8345
|
};
|
|
8197
8346
|
}
|
|
8198
8347
|
}
|
|
@@ -8237,8 +8386,8 @@ function calculateWaitTime(step, now) {
|
|
|
8237
8386
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
8238
8387
|
}
|
|
8239
8388
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
8240
|
-
const
|
|
8241
|
-
const windowScheduledTime =
|
|
8389
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
8390
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
8242
8391
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
8243
8392
|
if (waitFromNow < earliestWaitFromNow) {
|
|
8244
8393
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -8412,6 +8561,10 @@ export {
|
|
|
8412
8561
|
AssociationSchema,
|
|
8413
8562
|
AuthenticationError,
|
|
8414
8563
|
BaseEventPayloadSchema,
|
|
8564
|
+
BatchOperationDefinitionSchema,
|
|
8565
|
+
BatchOperationIterateResultSchema,
|
|
8566
|
+
BatchOperationSetupResultSchema,
|
|
8567
|
+
BatchPaginationSchema,
|
|
8415
8568
|
BreadcrumbItemSchema,
|
|
8416
8569
|
CRMCardinalitySchema,
|
|
8417
8570
|
CRMContextSchema2 as CRMContextSchema,
|
|
@@ -8664,6 +8817,7 @@ export {
|
|
|
8664
8817
|
src_default as default,
|
|
8665
8818
|
defaultShouldRetry,
|
|
8666
8819
|
defineAgent,
|
|
8820
|
+
defineBatchOperation,
|
|
8667
8821
|
defineChannel,
|
|
8668
8822
|
defineConfig,
|
|
8669
8823
|
defineEntity,
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ declare const _default: {
|
|
|
16
16
|
z: typeof z;
|
|
17
17
|
};
|
|
18
18
|
export default _default;
|
|
19
|
-
export { defineConfig, defineModel, defineEntity, defineChannel, definePage, defineWorkflow, defineAgent, defineEnv, defineNavigation, defineSetupStep, getAllEnvKeys, getRequiredInstallEnvKeys, } from './config';
|
|
19
|
+
export { defineConfig, defineModel, defineEntity, defineChannel, definePage, defineWorkflow, defineAgent, defineEnv, defineNavigation, defineSetupStep, defineBatchOperation, getAllEnvKeys, getRequiredInstallEnvKeys, } from './config';
|
|
20
20
|
export { queuedFetch, requeue, resolveQueue, createQueueHandle, queuedFetchResponse, registerQueueConfig, runWithRateLimitExecutionContext, getRateLimitExecutionContext, defaultShouldRetry, QueueContextError, QueueNotFoundError, QueuedFetchExhaustedError, RequeueOutsideContextError, RateLimitBackendError, RateLimitExceededError, } from './ratelimit';
|
|
21
21
|
export type { QueueInput, QueueSelector, Lease, QueueLimits, ResolvedQueue, RateLimitExecutionContext, RateLimitBackend, QueueScope, QueueConfig, SerializableQueueConfig, QueueRegistry, } from './ratelimit';
|
|
22
22
|
export { sequencer, allow as sequencerAllow, acquire as sequencerAcquire, release as sequencerRelease, resolveSequencer, createSequencerHandle, registerSequencerConfig, clearRegisteredSequencerConfig, getSequencerConfig, getSequencerDefinitions, resetSequencerBackendForTests, memorySequencerBackend, platformSequencerBackend, SequencerContextError, SequencerNotFoundError, SequencerBackendError, SequencerLockError, } from './sequencer';
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,10 @@ __export(index_exports, {
|
|
|
42
42
|
AssociationSchema: () => AssociationSchema,
|
|
43
43
|
AuthenticationError: () => AuthenticationError,
|
|
44
44
|
BaseEventPayloadSchema: () => BaseEventPayloadSchema,
|
|
45
|
+
BatchOperationDefinitionSchema: () => BatchOperationDefinitionSchema,
|
|
46
|
+
BatchOperationIterateResultSchema: () => BatchOperationIterateResultSchema,
|
|
47
|
+
BatchOperationSetupResultSchema: () => BatchOperationSetupResultSchema,
|
|
48
|
+
BatchPaginationSchema: () => BatchPaginationSchema,
|
|
45
49
|
BreadcrumbItemSchema: () => BreadcrumbItemSchema,
|
|
46
50
|
CRMCardinalitySchema: () => CRMCardinalitySchema,
|
|
47
51
|
CRMContextSchema: () => CRMContextSchema2,
|
|
@@ -294,6 +298,7 @@ __export(index_exports, {
|
|
|
294
298
|
default: () => index_default,
|
|
295
299
|
defaultShouldRetry: () => defaultShouldRetry,
|
|
296
300
|
defineAgent: () => defineAgent,
|
|
301
|
+
defineBatchOperation: () => defineBatchOperation,
|
|
297
302
|
defineChannel: () => defineChannel,
|
|
298
303
|
defineConfig: () => defineConfig,
|
|
299
304
|
defineEntity: () => defineEntity,
|
|
@@ -2275,6 +2280,31 @@ var SetupStepDefinitionSchema = import_v48.z.object({
|
|
|
2275
2280
|
listenToEnv: import_v48.z.boolean().optional(),
|
|
2276
2281
|
capabilities: import_v48.z.array(import_v48.z.string()).optional()
|
|
2277
2282
|
});
|
|
2283
|
+
var BatchPaginationSchema = import_v48.z.object({
|
|
2284
|
+
hasMore: import_v48.z.boolean(),
|
|
2285
|
+
page: import_v48.z.number().int().optional(),
|
|
2286
|
+
total: import_v48.z.number().int().optional(),
|
|
2287
|
+
nextCursor: import_v48.z.union([import_v48.z.string(), import_v48.z.number()]).optional(),
|
|
2288
|
+
limit: import_v48.z.number().int().positive().optional()
|
|
2289
|
+
});
|
|
2290
|
+
var BatchOperationSetupResultSchema = import_v48.z.object({
|
|
2291
|
+
state: import_v48.z.record(import_v48.z.string(), import_v48.z.unknown()).optional(),
|
|
2292
|
+
total: import_v48.z.number().int().nonnegative().optional()
|
|
2293
|
+
});
|
|
2294
|
+
var BatchOperationIterateResultSchema = import_v48.z.object({
|
|
2295
|
+
items: import_v48.z.array(import_v48.z.record(import_v48.z.string(), import_v48.z.unknown())),
|
|
2296
|
+
pagination: BatchPaginationSchema,
|
|
2297
|
+
state: import_v48.z.record(import_v48.z.string(), import_v48.z.unknown()).optional()
|
|
2298
|
+
});
|
|
2299
|
+
var BatchOperationDefinitionSchema = import_v48.z.object({
|
|
2300
|
+
handle: import_v48.z.string(),
|
|
2301
|
+
label: import_v48.z.string(),
|
|
2302
|
+
description: import_v48.z.string().optional(),
|
|
2303
|
+
entity: import_v48.z.string(),
|
|
2304
|
+
maxConcurrent: import_v48.z.number().int().positive().optional(),
|
|
2305
|
+
pageSize: import_v48.z.number().int().positive().optional(),
|
|
2306
|
+
icon: import_v48.z.string().optional()
|
|
2307
|
+
});
|
|
2278
2308
|
var ProvisionConfigSchema = import_v48.z.object({
|
|
2279
2309
|
env: EnvSchemaSchema.optional(),
|
|
2280
2310
|
/** INTERNAL model definitions (app-owned, not visible to users) */
|
|
@@ -2289,7 +2319,9 @@ var ProvisionConfigSchema = import_v48.z.object({
|
|
|
2289
2319
|
navigation: NavigationConfigSchema.optional(),
|
|
2290
2320
|
pages: import_v48.z.array(PageDefinitionSchema).optional(),
|
|
2291
2321
|
/** Install setup checklist steps (opt-in) */
|
|
2292
|
-
setup: import_v48.z.array(SetupStepDefinitionSchema).optional()
|
|
2322
|
+
setup: import_v48.z.array(SetupStepDefinitionSchema).optional(),
|
|
2323
|
+
/** Batch operations for imports, syncs, and long-running tasks */
|
|
2324
|
+
batchOperations: import_v48.z.array(BatchOperationDefinitionSchema).optional()
|
|
2293
2325
|
});
|
|
2294
2326
|
var SkedyulConfigSchema = import_v48.z.object({
|
|
2295
2327
|
name: import_v48.z.string(),
|
|
@@ -5811,6 +5843,107 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
5811
5843
|
}
|
|
5812
5844
|
};
|
|
5813
5845
|
}
|
|
5846
|
+
async function handleBatchOperationRoute(req, ctx) {
|
|
5847
|
+
const bodyResult = parseJsonBody(req);
|
|
5848
|
+
if (!bodyResult.success) {
|
|
5849
|
+
return bodyResult.error;
|
|
5850
|
+
}
|
|
5851
|
+
const body = bodyResult.data;
|
|
5852
|
+
const registry = ctx.batchOperationRegistry;
|
|
5853
|
+
if (!registry) {
|
|
5854
|
+
return {
|
|
5855
|
+
status: 404,
|
|
5856
|
+
body: {
|
|
5857
|
+
success: false,
|
|
5858
|
+
error: "No batch operations registered"
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
}
|
|
5862
|
+
const operation = registry[body.handle];
|
|
5863
|
+
if (!operation) {
|
|
5864
|
+
return {
|
|
5865
|
+
status: 404,
|
|
5866
|
+
body: {
|
|
5867
|
+
success: false,
|
|
5868
|
+
error: `Batch operation not found: ${body.handle}`
|
|
5869
|
+
}
|
|
5870
|
+
};
|
|
5871
|
+
}
|
|
5872
|
+
const log = {
|
|
5873
|
+
info: (message, meta) => {
|
|
5874
|
+
console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5875
|
+
},
|
|
5876
|
+
warn: (message, meta) => {
|
|
5877
|
+
console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5878
|
+
},
|
|
5879
|
+
error: (message, meta) => {
|
|
5880
|
+
console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
5881
|
+
}
|
|
5882
|
+
};
|
|
5883
|
+
try {
|
|
5884
|
+
if (body.method === "setup") {
|
|
5885
|
+
if (!operation.setup) {
|
|
5886
|
+
return {
|
|
5887
|
+
status: 200,
|
|
5888
|
+
body: {
|
|
5889
|
+
success: true,
|
|
5890
|
+
result: { state: {}, total: void 0 }
|
|
5891
|
+
}
|
|
5892
|
+
};
|
|
5893
|
+
}
|
|
5894
|
+
const result = await operation.setup({
|
|
5895
|
+
workplaceId: body.workplaceId,
|
|
5896
|
+
appInstallationId: body.appInstallationId,
|
|
5897
|
+
appId: body.appId,
|
|
5898
|
+
input: body.input,
|
|
5899
|
+
log
|
|
5900
|
+
});
|
|
5901
|
+
return {
|
|
5902
|
+
status: 200,
|
|
5903
|
+
body: {
|
|
5904
|
+
success: true,
|
|
5905
|
+
result
|
|
5906
|
+
}
|
|
5907
|
+
};
|
|
5908
|
+
}
|
|
5909
|
+
if (body.method === "iterate") {
|
|
5910
|
+
const result = await operation.iterate({
|
|
5911
|
+
workplaceId: body.workplaceId,
|
|
5912
|
+
appInstallationId: body.appInstallationId,
|
|
5913
|
+
appId: body.appId,
|
|
5914
|
+
input: body.input,
|
|
5915
|
+
state: body.state,
|
|
5916
|
+
page: body.page,
|
|
5917
|
+
cursor: body.cursor,
|
|
5918
|
+
limit: body.limit,
|
|
5919
|
+
log
|
|
5920
|
+
});
|
|
5921
|
+
return {
|
|
5922
|
+
status: 200,
|
|
5923
|
+
body: {
|
|
5924
|
+
success: true,
|
|
5925
|
+
result
|
|
5926
|
+
}
|
|
5927
|
+
};
|
|
5928
|
+
}
|
|
5929
|
+
return {
|
|
5930
|
+
status: 400,
|
|
5931
|
+
body: {
|
|
5932
|
+
success: false,
|
|
5933
|
+
error: `Unknown method: ${body.method}`
|
|
5934
|
+
}
|
|
5935
|
+
};
|
|
5936
|
+
} catch (err) {
|
|
5937
|
+
log.error("Batch operation failed", { error: err });
|
|
5938
|
+
return {
|
|
5939
|
+
status: 500,
|
|
5940
|
+
body: {
|
|
5941
|
+
success: false,
|
|
5942
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5943
|
+
}
|
|
5944
|
+
};
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5814
5947
|
function createNotFoundResponse() {
|
|
5815
5948
|
return {
|
|
5816
5949
|
status: 404,
|
|
@@ -5890,6 +6023,9 @@ async function routeRequest(req, ctx) {
|
|
|
5890
6023
|
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
5891
6024
|
return handleMcpBatchRoute(req, ctx);
|
|
5892
6025
|
}
|
|
6026
|
+
if (req.path === "/batch-operation" && req.method === "POST") {
|
|
6027
|
+
return handleBatchOperationRoute(req, ctx);
|
|
6028
|
+
}
|
|
5893
6029
|
return createNotFoundResponse();
|
|
5894
6030
|
} catch (err) {
|
|
5895
6031
|
return createErrorResponse2(err);
|
|
@@ -5931,11 +6067,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
5931
6067
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
5932
6068
|
const registry = config.tools;
|
|
5933
6069
|
const webhookRegistry = config.webhooks;
|
|
6070
|
+
const batchOperationRegistry = config.batchOperations;
|
|
5934
6071
|
const ctx = {
|
|
5935
6072
|
config,
|
|
5936
6073
|
tools,
|
|
5937
6074
|
registry,
|
|
5938
6075
|
webhookRegistry,
|
|
6076
|
+
batchOperationRegistry,
|
|
5939
6077
|
callTool,
|
|
5940
6078
|
state,
|
|
5941
6079
|
mcpServer,
|
|
@@ -6050,12 +6188,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
6050
6188
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
6051
6189
|
const registry = config.tools;
|
|
6052
6190
|
const webhookRegistry = config.webhooks;
|
|
6191
|
+
const batchOperationRegistry = config.batchOperations;
|
|
6053
6192
|
let hasLoggedStartup = false;
|
|
6054
6193
|
const ctx = {
|
|
6055
6194
|
config,
|
|
6056
6195
|
tools,
|
|
6057
6196
|
registry,
|
|
6058
6197
|
webhookRegistry,
|
|
6198
|
+
batchOperationRegistry,
|
|
6059
6199
|
callTool,
|
|
6060
6200
|
state,
|
|
6061
6201
|
mcpServer,
|
|
@@ -6353,6 +6493,9 @@ function defineNavigation(navigation) {
|
|
|
6353
6493
|
function defineSetupStep(step) {
|
|
6354
6494
|
return step;
|
|
6355
6495
|
}
|
|
6496
|
+
function defineBatchOperation(operation) {
|
|
6497
|
+
return operation;
|
|
6498
|
+
}
|
|
6356
6499
|
|
|
6357
6500
|
// src/config/schema-loader.ts
|
|
6358
6501
|
var fs4 = __toESM(require("fs"));
|
|
@@ -8401,7 +8544,15 @@ var WaitInputRelativeSchema = import_v413.z.object({
|
|
|
8401
8544
|
mode: import_v413.z.literal("relative"),
|
|
8402
8545
|
amount: import_v413.z.number(),
|
|
8403
8546
|
unit: WaitUnitSchema,
|
|
8404
|
-
windows: import_v413.z.array(TimeWindowSlotSchema).optional()
|
|
8547
|
+
windows: import_v413.z.array(TimeWindowSlotSchema).optional(),
|
|
8548
|
+
/**
|
|
8549
|
+
* Optional ISO datetime to use as the base for the relative delay instead of
|
|
8550
|
+
* wall-clock `now`. Used for cadence chaining (schedule relative to a prior
|
|
8551
|
+
* message's scheduledAt). `waitTime` is still measured from wall-clock `now`.
|
|
8552
|
+
*/
|
|
8553
|
+
from: import_v413.z.union([import_v413.z.string(), import_v413.z.date()]).optional().describe(
|
|
8554
|
+
"ISO datetime base for the relative delay (e.g. previous message scheduledAt)"
|
|
8555
|
+
)
|
|
8405
8556
|
});
|
|
8406
8557
|
var WaitInputAbsoluteSchema = import_v413.z.object({
|
|
8407
8558
|
mode: import_v413.z.literal("absolute"),
|
|
@@ -8514,10 +8665,12 @@ function calculateWaitTime(step, now) {
|
|
|
8514
8665
|
};
|
|
8515
8666
|
}
|
|
8516
8667
|
case "relative": {
|
|
8668
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
8669
|
+
const baseDate = new Date(baseTime);
|
|
8517
8670
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
8518
8671
|
return {
|
|
8519
|
-
waitTime: 0,
|
|
8520
|
-
scheduledAt:
|
|
8672
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
8673
|
+
scheduledAt: baseDate
|
|
8521
8674
|
};
|
|
8522
8675
|
}
|
|
8523
8676
|
let relativeDelay = 0;
|
|
@@ -8556,17 +8709,18 @@ function calculateWaitTime(step, now) {
|
|
|
8556
8709
|
}
|
|
8557
8710
|
}
|
|
8558
8711
|
if (!step.windows || step.windows.length === 0) {
|
|
8712
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
8559
8713
|
return {
|
|
8560
|
-
waitTime:
|
|
8561
|
-
scheduledAt
|
|
8714
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
8715
|
+
scheduledAt
|
|
8562
8716
|
};
|
|
8563
8717
|
}
|
|
8564
|
-
const targetDate = new Date(
|
|
8718
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
8565
8719
|
for (const window of step.windows) {
|
|
8566
8720
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
8567
8721
|
return {
|
|
8568
|
-
waitTime:
|
|
8569
|
-
scheduledAt:
|
|
8722
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
8723
|
+
scheduledAt: targetDate
|
|
8570
8724
|
};
|
|
8571
8725
|
}
|
|
8572
8726
|
}
|
|
@@ -8611,8 +8765,8 @@ function calculateWaitTime(step, now) {
|
|
|
8611
8765
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
8612
8766
|
}
|
|
8613
8767
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
8614
|
-
const
|
|
8615
|
-
const windowScheduledTime =
|
|
8768
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
8769
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
8616
8770
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
8617
8771
|
if (waitFromNow < earliestWaitFromNow) {
|
|
8618
8772
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -8787,6 +8941,10 @@ var index_default = { z: import_v414.z };
|
|
|
8787
8941
|
AssociationSchema,
|
|
8788
8942
|
AuthenticationError,
|
|
8789
8943
|
BaseEventPayloadSchema,
|
|
8944
|
+
BatchOperationDefinitionSchema,
|
|
8945
|
+
BatchOperationIterateResultSchema,
|
|
8946
|
+
BatchOperationSetupResultSchema,
|
|
8947
|
+
BatchPaginationSchema,
|
|
8790
8948
|
BreadcrumbItemSchema,
|
|
8791
8949
|
CRMCardinalitySchema,
|
|
8792
8950
|
CRMContextSchema,
|
|
@@ -9038,6 +9196,7 @@ var index_default = { z: import_v414.z };
|
|
|
9038
9196
|
cron,
|
|
9039
9197
|
defaultShouldRetry,
|
|
9040
9198
|
defineAgent,
|
|
9199
|
+
defineBatchOperation,
|
|
9041
9200
|
defineChannel,
|
|
9042
9201
|
defineConfig,
|
|
9043
9202
|
defineEntity,
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
import type { WaitInputType, CalculateWaitTimeResult } from './types-workflow';
|
|
10
10
|
/**
|
|
11
11
|
* Calculate the wait time for a wait step
|
|
12
|
-
* @param step The input parameters for the wait step
|
|
13
|
-
*
|
|
12
|
+
* @param step The input parameters for the wait step. Relative mode may include
|
|
13
|
+
* optional `from` (ISO datetime / Date) as the base for the delay instead of `now`.
|
|
14
|
+
* @param now Wall-clock current date/time. Used as the default relative base and
|
|
15
|
+
* always used to compute `waitTime` (ms until `scheduledAt`).
|
|
14
16
|
* @returns The time to wait in milliseconds and the scheduled date
|
|
15
17
|
*/
|
|
16
18
|
export declare function calculateWaitTime(step: WaitInputType, now: Date): CalculateWaitTimeResult;
|
package/dist/scheduling/index.js
CHANGED
|
@@ -128,10 +128,12 @@ function calculateWaitTime(step, now) {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
case "relative": {
|
|
131
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
132
|
+
const baseDate = new Date(baseTime);
|
|
131
133
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
132
134
|
return {
|
|
133
|
-
waitTime: 0,
|
|
134
|
-
scheduledAt:
|
|
135
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
136
|
+
scheduledAt: baseDate
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
let relativeDelay = 0;
|
|
@@ -170,17 +172,18 @@ function calculateWaitTime(step, now) {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
if (!step.windows || step.windows.length === 0) {
|
|
175
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
173
176
|
return {
|
|
174
|
-
waitTime:
|
|
175
|
-
scheduledAt
|
|
177
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
178
|
+
scheduledAt
|
|
176
179
|
};
|
|
177
180
|
}
|
|
178
|
-
const targetDate = new Date(
|
|
181
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
179
182
|
for (const window of step.windows) {
|
|
180
183
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
181
184
|
return {
|
|
182
|
-
waitTime:
|
|
183
|
-
scheduledAt:
|
|
185
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
186
|
+
scheduledAt: targetDate
|
|
184
187
|
};
|
|
185
188
|
}
|
|
186
189
|
}
|
|
@@ -225,8 +228,8 @@ function calculateWaitTime(step, now) {
|
|
|
225
228
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
226
229
|
}
|
|
227
230
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
228
|
-
const
|
|
229
|
-
const windowScheduledTime =
|
|
231
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
232
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
230
233
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
231
234
|
if (waitFromNow < earliestWaitFromNow) {
|
|
232
235
|
earliestWaitFromNow = waitFromNow;
|