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
|
@@ -100,10 +100,12 @@ function calculateWaitTime(step, now) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
case "relative": {
|
|
103
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
104
|
+
const baseDate = new Date(baseTime);
|
|
103
105
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
104
106
|
return {
|
|
105
|
-
waitTime: 0,
|
|
106
|
-
scheduledAt:
|
|
107
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
108
|
+
scheduledAt: baseDate
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
let relativeDelay = 0;
|
|
@@ -142,17 +144,18 @@ function calculateWaitTime(step, now) {
|
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
if (!step.windows || step.windows.length === 0) {
|
|
147
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
145
148
|
return {
|
|
146
|
-
waitTime:
|
|
147
|
-
scheduledAt
|
|
149
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
150
|
+
scheduledAt
|
|
148
151
|
};
|
|
149
152
|
}
|
|
150
|
-
const targetDate = new Date(
|
|
153
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
151
154
|
for (const window of step.windows) {
|
|
152
155
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
153
156
|
return {
|
|
154
|
-
waitTime:
|
|
155
|
-
scheduledAt:
|
|
157
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
158
|
+
scheduledAt: targetDate
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
}
|
|
@@ -197,8 +200,8 @@ function calculateWaitTime(step, now) {
|
|
|
197
200
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
198
201
|
}
|
|
199
202
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
200
|
-
const
|
|
201
|
-
const windowScheduledTime =
|
|
203
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
204
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
202
205
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
203
206
|
if (waitFromNow < earliestWaitFromNow) {
|
|
204
207
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -33,6 +33,13 @@ export interface WaitInputRelative {
|
|
|
33
33
|
amount: number;
|
|
34
34
|
unit: WaitUnit;
|
|
35
35
|
windows?: TimeWindowSlot[];
|
|
36
|
+
/**
|
|
37
|
+
* Optional ISO datetime (or Date) to use as the base for the relative delay
|
|
38
|
+
* instead of wall-clock `now`. Used for cadence chaining: schedule message N
|
|
39
|
+
* relative to message N-1's `scheduledAt`.
|
|
40
|
+
* `waitTime` in the result is still measured from wall-clock `now`.
|
|
41
|
+
*/
|
|
42
|
+
from?: string | Date;
|
|
36
43
|
}
|
|
37
44
|
export interface WaitInputAbsolute {
|
|
38
45
|
mode: 'absolute';
|
|
@@ -107,6 +107,7 @@ export declare const WaitInputRelativeSchema: z.ZodObject<{
|
|
|
107
107
|
}>>;
|
|
108
108
|
timezone: z.ZodOptional<z.ZodString>;
|
|
109
109
|
}, z.core.$strip>>>;
|
|
110
|
+
from: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
110
111
|
}, z.core.$strip>;
|
|
111
112
|
export declare const WaitInputAbsoluteSchema: z.ZodObject<{
|
|
112
113
|
mode: z.ZodLiteral<"absolute">;
|
|
@@ -153,6 +154,7 @@ export declare const WaitInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
153
154
|
}>>;
|
|
154
155
|
timezone: z.ZodOptional<z.ZodString>;
|
|
155
156
|
}, z.core.$strip>>>;
|
|
157
|
+
from: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
156
158
|
}, z.core.$strip>, z.ZodObject<{
|
|
157
159
|
mode: z.ZodLiteral<"absolute">;
|
|
158
160
|
scheduleAt: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
package/dist/schemas.d.ts
CHANGED
|
@@ -3357,6 +3357,37 @@ export declare const SetupStepDefinitionSchema: z.ZodObject<{
|
|
|
3357
3357
|
listenToEnv: z.ZodOptional<z.ZodBoolean>;
|
|
3358
3358
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
3359
3359
|
}, z.core.$strip>;
|
|
3360
|
+
export declare const BatchPaginationSchema: z.ZodObject<{
|
|
3361
|
+
hasMore: z.ZodBoolean;
|
|
3362
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
3363
|
+
total: z.ZodOptional<z.ZodNumber>;
|
|
3364
|
+
nextCursor: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
3365
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
3366
|
+
}, z.core.$strip>;
|
|
3367
|
+
export declare const BatchOperationSetupResultSchema: z.ZodObject<{
|
|
3368
|
+
state: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
3369
|
+
total: z.ZodOptional<z.ZodNumber>;
|
|
3370
|
+
}, z.core.$strip>;
|
|
3371
|
+
export declare const BatchOperationIterateResultSchema: z.ZodObject<{
|
|
3372
|
+
items: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
3373
|
+
pagination: z.ZodObject<{
|
|
3374
|
+
hasMore: z.ZodBoolean;
|
|
3375
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
3376
|
+
total: z.ZodOptional<z.ZodNumber>;
|
|
3377
|
+
nextCursor: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
3378
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
3379
|
+
}, z.core.$strip>;
|
|
3380
|
+
state: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
3381
|
+
}, z.core.$strip>;
|
|
3382
|
+
export declare const BatchOperationDefinitionSchema: z.ZodObject<{
|
|
3383
|
+
handle: z.ZodString;
|
|
3384
|
+
label: z.ZodString;
|
|
3385
|
+
description: z.ZodOptional<z.ZodString>;
|
|
3386
|
+
entity: z.ZodString;
|
|
3387
|
+
maxConcurrent: z.ZodOptional<z.ZodNumber>;
|
|
3388
|
+
pageSize: z.ZodOptional<z.ZodNumber>;
|
|
3389
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
3390
|
+
}, z.core.$strip>;
|
|
3360
3391
|
export declare const ProvisionConfigSchema: z.ZodObject<{
|
|
3361
3392
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
3362
3393
|
label: z.ZodString;
|
|
@@ -4161,6 +4192,15 @@ export declare const ProvisionConfigSchema: z.ZodObject<{
|
|
|
4161
4192
|
listenToEnv: z.ZodOptional<z.ZodBoolean>;
|
|
4162
4193
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
4163
4194
|
}, z.core.$strip>>>;
|
|
4195
|
+
batchOperations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4196
|
+
handle: z.ZodString;
|
|
4197
|
+
label: z.ZodString;
|
|
4198
|
+
description: z.ZodOptional<z.ZodString>;
|
|
4199
|
+
entity: z.ZodString;
|
|
4200
|
+
maxConcurrent: z.ZodOptional<z.ZodNumber>;
|
|
4201
|
+
pageSize: z.ZodOptional<z.ZodNumber>;
|
|
4202
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
4203
|
+
}, z.core.$strip>>>;
|
|
4164
4204
|
}, z.core.$strip>;
|
|
4165
4205
|
export declare const SkedyulConfigSchema: z.ZodObject<{
|
|
4166
4206
|
name: z.ZodString;
|
|
@@ -4976,6 +5016,15 @@ export declare const SkedyulConfigSchema: z.ZodObject<{
|
|
|
4976
5016
|
listenToEnv: z.ZodOptional<z.ZodBoolean>;
|
|
4977
5017
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
4978
5018
|
}, z.core.$strip>>>;
|
|
5019
|
+
batchOperations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
5020
|
+
handle: z.ZodString;
|
|
5021
|
+
label: z.ZodString;
|
|
5022
|
+
description: z.ZodOptional<z.ZodString>;
|
|
5023
|
+
entity: z.ZodString;
|
|
5024
|
+
maxConcurrent: z.ZodOptional<z.ZodNumber>;
|
|
5025
|
+
pageSize: z.ZodOptional<z.ZodNumber>;
|
|
5026
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
5027
|
+
}, z.core.$strip>>>;
|
|
4979
5028
|
}, z.core.$strip>, z.ZodUnknown]>>;
|
|
4980
5029
|
agents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
4981
5030
|
handle: z.ZodString;
|
|
@@ -5039,6 +5088,10 @@ export type InstallConfig = z.infer<typeof InstallConfigSchema>;
|
|
|
5039
5088
|
export type ProvisionConfig = z.infer<typeof ProvisionConfigSchema>;
|
|
5040
5089
|
export type SetupStepKind = z.infer<typeof SetupStepKindSchema>;
|
|
5041
5090
|
export type SetupStepDefinition = z.infer<typeof SetupStepDefinitionSchema>;
|
|
5091
|
+
export type BatchPagination = z.infer<typeof BatchPaginationSchema>;
|
|
5092
|
+
export type BatchOperationSetupResult = z.infer<typeof BatchOperationSetupResultSchema>;
|
|
5093
|
+
export type BatchOperationIterateResult = z.infer<typeof BatchOperationIterateResultSchema>;
|
|
5094
|
+
export type BatchOperationDefinition = z.infer<typeof BatchOperationDefinitionSchema>;
|
|
5042
5095
|
export type FormV2StyleProps = z.infer<typeof FormV2StylePropsSchema>;
|
|
5043
5096
|
export type FieldSettingButtonProps = z.infer<typeof FieldSettingButtonPropsSchema>;
|
|
5044
5097
|
export type RelationshipExtension = z.infer<typeof RelationshipExtensionSchema>;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DedicatedServerInstance, ServerlessServerInstance, SkedyulServerInstance, ToolRegistry, WebhookRegistry } from '../types';
|
|
1
|
+
import type { DedicatedServerInstance, ServerlessServerInstance, SkedyulServerInstance, ToolRegistry, WebhookRegistry, BatchOperationRegistry } from '../types';
|
|
2
2
|
import type { SkedyulConfig } from '../config/app-config';
|
|
3
3
|
export type { RequestState, CoreMethod, ToolCallArgs } from './types';
|
|
4
4
|
export { normalizeBilling, toJsonSchema, isToolSchemaWithJson, getZodSchema, getJsonSchemaFromToolSchema, parseJsonRecord, parseNumberEnv, mergeRuntimeEnv, readRawRequestBody, parseJSONBody, sendJSON, sendHTML, getDefaultHeaders, createResponse, getListeningPort, } from './utils';
|
|
@@ -15,9 +15,10 @@ export type { UnifiedRequest, UnifiedResponse, RouteContext } from './route-hand
|
|
|
15
15
|
* Runtime config type - SkedyulConfig with resolved registries (not promises).
|
|
16
16
|
* This is what server.create() expects at runtime.
|
|
17
17
|
*/
|
|
18
|
-
export type RuntimeSkedyulConfig = Omit<SkedyulConfig, 'tools' | 'webhooks'> & {
|
|
18
|
+
export type RuntimeSkedyulConfig = Omit<SkedyulConfig, 'tools' | 'webhooks' | 'batchOperations'> & {
|
|
19
19
|
tools: ToolRegistry;
|
|
20
20
|
webhooks?: WebhookRegistry;
|
|
21
|
+
batchOperations?: BatchOperationRegistry;
|
|
21
22
|
};
|
|
22
23
|
export declare function createSkedyulServer(config: RuntimeSkedyulConfig & {
|
|
23
24
|
computeLayer: 'dedicated';
|
|
@@ -60,6 +60,10 @@ export declare function handleMcpRoute(req: UnifiedRequest, ctx: RouteContext):
|
|
|
60
60
|
* This is used by the message outbox processor for efficient batched delivery.
|
|
61
61
|
*/
|
|
62
62
|
export declare function handleMcpBatchRoute(req: UnifiedRequest, ctx: RouteContext): Promise<UnifiedResponse>;
|
|
63
|
+
/**
|
|
64
|
+
* Handle /batch-operation route for invoking batch operation setup/iterate.
|
|
65
|
+
*/
|
|
66
|
+
export declare function handleBatchOperationRoute(req: UnifiedRequest, ctx: RouteContext): Promise<UnifiedResponse>;
|
|
63
67
|
/**
|
|
64
68
|
* Create a 404 Not Found response in JSON-RPC format.
|
|
65
69
|
*/
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export type { UnifiedRequest, UnifiedResponse, RouteContext, CallToolFn, ParsedJsonRpcBody, SkedyulToolCallArgs, ParseResult, } from './types';
|
|
8
8
|
export { fromLambdaEvent, toLambdaResponse, parseJsonBody, parseJsonRpcBody, getHeader, getContentType, parseBodyByContentType, } from './adapters';
|
|
9
|
-
export { handleHealthRoute, handleConfigRoute, handleCoreRoute, handleCoreWebhookRoute, handleEstimateRoute, handleInstallRoute, handleUninstallRoute, handleProvisionRoute, handleSetupRevalidateRoute, handleOAuthCallbackRoute, handleWebhookRoute, handleMcpRoute, createNotFoundResponse, createOptionsResponse, createErrorResponse, } from './handlers';
|
|
9
|
+
export { handleHealthRoute, handleConfigRoute, handleCoreRoute, handleCoreWebhookRoute, handleEstimateRoute, handleInstallRoute, handleUninstallRoute, handleProvisionRoute, handleSetupRevalidateRoute, handleOAuthCallbackRoute, handleWebhookRoute, handleMcpRoute, handleBatchOperationRoute, createNotFoundResponse, createOptionsResponse, createErrorResponse, } from './handlers';
|
|
10
10
|
export { routeRequest } from './router';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* allowing route handlers to be transport-agnostic.
|
|
6
6
|
*/
|
|
7
7
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
-
import type { ToolCallResponse, ToolMetadata, ToolRegistry, WebhookRegistry, InvocationContext } from '../../types';
|
|
8
|
+
import type { ToolCallResponse, ToolMetadata, ToolRegistry, WebhookRegistry, BatchOperationRegistry, InvocationContext } from '../../types';
|
|
9
9
|
import type { RuntimeSkedyulConfig } from '../index';
|
|
10
10
|
import type { RequestState } from '../types';
|
|
11
11
|
/**
|
|
@@ -39,6 +39,7 @@ export interface RouteContext {
|
|
|
39
39
|
tools: ToolMetadata[];
|
|
40
40
|
registry: ToolRegistry;
|
|
41
41
|
webhookRegistry?: WebhookRegistry;
|
|
42
|
+
batchOperationRegistry?: BatchOperationRegistry;
|
|
42
43
|
callTool: CallToolFn;
|
|
43
44
|
state: RequestState;
|
|
44
45
|
mcpServer: McpServer;
|
package/dist/server.js
CHANGED
|
@@ -1988,6 +1988,107 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
1988
1988
|
}
|
|
1989
1989
|
};
|
|
1990
1990
|
}
|
|
1991
|
+
async function handleBatchOperationRoute(req, ctx) {
|
|
1992
|
+
const bodyResult = parseJsonBody(req);
|
|
1993
|
+
if (!bodyResult.success) {
|
|
1994
|
+
return bodyResult.error;
|
|
1995
|
+
}
|
|
1996
|
+
const body = bodyResult.data;
|
|
1997
|
+
const registry = ctx.batchOperationRegistry;
|
|
1998
|
+
if (!registry) {
|
|
1999
|
+
return {
|
|
2000
|
+
status: 404,
|
|
2001
|
+
body: {
|
|
2002
|
+
success: false,
|
|
2003
|
+
error: "No batch operations registered"
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
const operation = registry[body.handle];
|
|
2008
|
+
if (!operation) {
|
|
2009
|
+
return {
|
|
2010
|
+
status: 404,
|
|
2011
|
+
body: {
|
|
2012
|
+
success: false,
|
|
2013
|
+
error: `Batch operation not found: ${body.handle}`
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
const log = {
|
|
2018
|
+
info: (message, meta) => {
|
|
2019
|
+
console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
2020
|
+
},
|
|
2021
|
+
warn: (message, meta) => {
|
|
2022
|
+
console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
2023
|
+
},
|
|
2024
|
+
error: (message, meta) => {
|
|
2025
|
+
console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
try {
|
|
2029
|
+
if (body.method === "setup") {
|
|
2030
|
+
if (!operation.setup) {
|
|
2031
|
+
return {
|
|
2032
|
+
status: 200,
|
|
2033
|
+
body: {
|
|
2034
|
+
success: true,
|
|
2035
|
+
result: { state: {}, total: void 0 }
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
const result = await operation.setup({
|
|
2040
|
+
workplaceId: body.workplaceId,
|
|
2041
|
+
appInstallationId: body.appInstallationId,
|
|
2042
|
+
appId: body.appId,
|
|
2043
|
+
input: body.input,
|
|
2044
|
+
log
|
|
2045
|
+
});
|
|
2046
|
+
return {
|
|
2047
|
+
status: 200,
|
|
2048
|
+
body: {
|
|
2049
|
+
success: true,
|
|
2050
|
+
result
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
if (body.method === "iterate") {
|
|
2055
|
+
const result = await operation.iterate({
|
|
2056
|
+
workplaceId: body.workplaceId,
|
|
2057
|
+
appInstallationId: body.appInstallationId,
|
|
2058
|
+
appId: body.appId,
|
|
2059
|
+
input: body.input,
|
|
2060
|
+
state: body.state,
|
|
2061
|
+
page: body.page,
|
|
2062
|
+
cursor: body.cursor,
|
|
2063
|
+
limit: body.limit,
|
|
2064
|
+
log
|
|
2065
|
+
});
|
|
2066
|
+
return {
|
|
2067
|
+
status: 200,
|
|
2068
|
+
body: {
|
|
2069
|
+
success: true,
|
|
2070
|
+
result
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
return {
|
|
2075
|
+
status: 400,
|
|
2076
|
+
body: {
|
|
2077
|
+
success: false,
|
|
2078
|
+
error: `Unknown method: ${body.method}`
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
} catch (err) {
|
|
2082
|
+
log.error("Batch operation failed", { error: err });
|
|
2083
|
+
return {
|
|
2084
|
+
status: 500,
|
|
2085
|
+
body: {
|
|
2086
|
+
success: false,
|
|
2087
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
1991
2092
|
function createNotFoundResponse() {
|
|
1992
2093
|
return {
|
|
1993
2094
|
status: 404,
|
|
@@ -2067,6 +2168,9 @@ async function routeRequest(req, ctx) {
|
|
|
2067
2168
|
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
2068
2169
|
return handleMcpBatchRoute(req, ctx);
|
|
2069
2170
|
}
|
|
2171
|
+
if (req.path === "/batch-operation" && req.method === "POST") {
|
|
2172
|
+
return handleBatchOperationRoute(req, ctx);
|
|
2173
|
+
}
|
|
2070
2174
|
return createNotFoundResponse();
|
|
2071
2175
|
} catch (err) {
|
|
2072
2176
|
return createErrorResponse(err);
|
|
@@ -2108,11 +2212,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
2108
2212
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
2109
2213
|
const registry = config.tools;
|
|
2110
2214
|
const webhookRegistry = config.webhooks;
|
|
2215
|
+
const batchOperationRegistry = config.batchOperations;
|
|
2111
2216
|
const ctx = {
|
|
2112
2217
|
config,
|
|
2113
2218
|
tools,
|
|
2114
2219
|
registry,
|
|
2115
2220
|
webhookRegistry,
|
|
2221
|
+
batchOperationRegistry,
|
|
2116
2222
|
callTool,
|
|
2117
2223
|
state,
|
|
2118
2224
|
mcpServer,
|
|
@@ -2227,12 +2333,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
2227
2333
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
2228
2334
|
const registry = config.tools;
|
|
2229
2335
|
const webhookRegistry = config.webhooks;
|
|
2336
|
+
const batchOperationRegistry = config.batchOperations;
|
|
2230
2337
|
let hasLoggedStartup = false;
|
|
2231
2338
|
const ctx = {
|
|
2232
2339
|
config,
|
|
2233
2340
|
tools,
|
|
2234
2341
|
registry,
|
|
2235
2342
|
webhookRegistry,
|
|
2343
|
+
batchOperationRegistry,
|
|
2236
2344
|
callTool,
|
|
2237
2345
|
state,
|
|
2238
2346
|
mcpServer,
|
|
@@ -1949,6 +1949,107 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
1949
1949
|
}
|
|
1950
1950
|
};
|
|
1951
1951
|
}
|
|
1952
|
+
async function handleBatchOperationRoute(req, ctx) {
|
|
1953
|
+
const bodyResult = parseJsonBody(req);
|
|
1954
|
+
if (!bodyResult.success) {
|
|
1955
|
+
return bodyResult.error;
|
|
1956
|
+
}
|
|
1957
|
+
const body = bodyResult.data;
|
|
1958
|
+
const registry = ctx.batchOperationRegistry;
|
|
1959
|
+
if (!registry) {
|
|
1960
|
+
return {
|
|
1961
|
+
status: 404,
|
|
1962
|
+
body: {
|
|
1963
|
+
success: false,
|
|
1964
|
+
error: "No batch operations registered"
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
const operation = registry[body.handle];
|
|
1969
|
+
if (!operation) {
|
|
1970
|
+
return {
|
|
1971
|
+
status: 404,
|
|
1972
|
+
body: {
|
|
1973
|
+
success: false,
|
|
1974
|
+
error: `Batch operation not found: ${body.handle}`
|
|
1975
|
+
}
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
const log = {
|
|
1979
|
+
info: (message, meta) => {
|
|
1980
|
+
console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
1981
|
+
},
|
|
1982
|
+
warn: (message, meta) => {
|
|
1983
|
+
console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
1984
|
+
},
|
|
1985
|
+
error: (message, meta) => {
|
|
1986
|
+
console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
try {
|
|
1990
|
+
if (body.method === "setup") {
|
|
1991
|
+
if (!operation.setup) {
|
|
1992
|
+
return {
|
|
1993
|
+
status: 200,
|
|
1994
|
+
body: {
|
|
1995
|
+
success: true,
|
|
1996
|
+
result: { state: {}, total: void 0 }
|
|
1997
|
+
}
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
const result = await operation.setup({
|
|
2001
|
+
workplaceId: body.workplaceId,
|
|
2002
|
+
appInstallationId: body.appInstallationId,
|
|
2003
|
+
appId: body.appId,
|
|
2004
|
+
input: body.input,
|
|
2005
|
+
log
|
|
2006
|
+
});
|
|
2007
|
+
return {
|
|
2008
|
+
status: 200,
|
|
2009
|
+
body: {
|
|
2010
|
+
success: true,
|
|
2011
|
+
result
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
if (body.method === "iterate") {
|
|
2016
|
+
const result = await operation.iterate({
|
|
2017
|
+
workplaceId: body.workplaceId,
|
|
2018
|
+
appInstallationId: body.appInstallationId,
|
|
2019
|
+
appId: body.appId,
|
|
2020
|
+
input: body.input,
|
|
2021
|
+
state: body.state,
|
|
2022
|
+
page: body.page,
|
|
2023
|
+
cursor: body.cursor,
|
|
2024
|
+
limit: body.limit,
|
|
2025
|
+
log
|
|
2026
|
+
});
|
|
2027
|
+
return {
|
|
2028
|
+
status: 200,
|
|
2029
|
+
body: {
|
|
2030
|
+
success: true,
|
|
2031
|
+
result
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
return {
|
|
2036
|
+
status: 400,
|
|
2037
|
+
body: {
|
|
2038
|
+
success: false,
|
|
2039
|
+
error: `Unknown method: ${body.method}`
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
} catch (err) {
|
|
2043
|
+
log.error("Batch operation failed", { error: err });
|
|
2044
|
+
return {
|
|
2045
|
+
status: 500,
|
|
2046
|
+
body: {
|
|
2047
|
+
success: false,
|
|
2048
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
1952
2053
|
function createNotFoundResponse() {
|
|
1953
2054
|
return {
|
|
1954
2055
|
status: 404,
|
|
@@ -2028,6 +2129,9 @@ async function routeRequest(req, ctx) {
|
|
|
2028
2129
|
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
2029
2130
|
return handleMcpBatchRoute(req, ctx);
|
|
2030
2131
|
}
|
|
2132
|
+
if (req.path === "/batch-operation" && req.method === "POST") {
|
|
2133
|
+
return handleBatchOperationRoute(req, ctx);
|
|
2134
|
+
}
|
|
2031
2135
|
return createNotFoundResponse();
|
|
2032
2136
|
} catch (err) {
|
|
2033
2137
|
return createErrorResponse(err);
|
|
@@ -2069,11 +2173,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
2069
2173
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
2070
2174
|
const registry = config.tools;
|
|
2071
2175
|
const webhookRegistry = config.webhooks;
|
|
2176
|
+
const batchOperationRegistry = config.batchOperations;
|
|
2072
2177
|
const ctx = {
|
|
2073
2178
|
config,
|
|
2074
2179
|
tools,
|
|
2075
2180
|
registry,
|
|
2076
2181
|
webhookRegistry,
|
|
2182
|
+
batchOperationRegistry,
|
|
2077
2183
|
callTool,
|
|
2078
2184
|
state,
|
|
2079
2185
|
mcpServer,
|
|
@@ -2188,12 +2294,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
2188
2294
|
const defaultHeaders = getDefaultHeaders(config.cors);
|
|
2189
2295
|
const registry = config.tools;
|
|
2190
2296
|
const webhookRegistry = config.webhooks;
|
|
2297
|
+
const batchOperationRegistry = config.batchOperations;
|
|
2191
2298
|
let hasLoggedStartup = false;
|
|
2192
2299
|
const ctx = {
|
|
2193
2300
|
config,
|
|
2194
2301
|
tools,
|
|
2195
2302
|
registry,
|
|
2196
2303
|
webhookRegistry,
|
|
2304
|
+
batchOperationRegistry,
|
|
2197
2305
|
callTool,
|
|
2198
2306
|
state,
|
|
2199
2307
|
mcpServer,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch operation runtime types.
|
|
3
|
+
*
|
|
4
|
+
* These types define the registry and context for batch operations at runtime.
|
|
5
|
+
*/
|
|
6
|
+
import type { BatchOperationSetupFn, BatchOperationIterateFn } from '../config/types/batch-operation';
|
|
7
|
+
export type { BatchOperationContext } from '../config/types/batch-operation';
|
|
8
|
+
/**
|
|
9
|
+
* Entry in the batch operation registry.
|
|
10
|
+
* Contains both metadata and function implementations.
|
|
11
|
+
*/
|
|
12
|
+
export interface BatchOperationRegistryEntry {
|
|
13
|
+
/** Unique handle for this operation */
|
|
14
|
+
handle: string;
|
|
15
|
+
/** Human-readable label */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Description of what this operation does */
|
|
18
|
+
description?: string;
|
|
19
|
+
/** Entity handle that items will be upserted to */
|
|
20
|
+
entity: string;
|
|
21
|
+
/** Setup function called once at the start */
|
|
22
|
+
setup?: BatchOperationSetupFn;
|
|
23
|
+
/** Iterate function called for each page */
|
|
24
|
+
iterate: BatchOperationIterateFn;
|
|
25
|
+
/** Maximum concurrent operations per app installation */
|
|
26
|
+
maxConcurrent?: number;
|
|
27
|
+
/** Default page size */
|
|
28
|
+
pageSize?: number;
|
|
29
|
+
/** Optional icon */
|
|
30
|
+
icon?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registry mapping batch operation handles to their implementations.
|
|
34
|
+
*/
|
|
35
|
+
export type BatchOperationRegistry = Record<string, BatchOperationRegistryEntry>;
|
|
36
|
+
/**
|
|
37
|
+
* Metadata for a batch operation (serializable, no functions).
|
|
38
|
+
*/
|
|
39
|
+
export interface BatchOperationMetadata {
|
|
40
|
+
handle: string;
|
|
41
|
+
label: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
entity: string;
|
|
44
|
+
maxConcurrent?: number;
|
|
45
|
+
pageSize?: number;
|
|
46
|
+
icon?: string;
|
|
47
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -22,3 +22,4 @@ export type { WebhookWireRequest, HandlerRawRequest, WebhookRequest, WebhookResp
|
|
|
22
22
|
export { isRuntimeWebhookContext } from './webhook';
|
|
23
23
|
export type { MCPError, MCPContentItem, MCPResult, MCPResponse, MCPRequest, } from './mcp-protocol';
|
|
24
24
|
export type { DataBlock, DataBlockLink, SpreadsheetBlock, SpreadsheetColumn, ProfileBlock, ProfileAvatar, ProfileField, FieldChangesBlock, FieldChangeItem, DateTimeBlock, } from './data-blocks';
|
|
25
|
+
export type { BatchOperationContext, BatchOperationRegistryEntry, BatchOperationRegistry, BatchOperationMetadata, } from './batch-operation';
|