skedyul 1.7.8 → 1.7.11

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/index.js CHANGED
@@ -42,6 +42,7 @@ __export(index_exports, {
42
42
  AssociationSchema: () => AssociationSchema,
43
43
  AuthenticationError: () => AuthenticationError,
44
44
  BaseEventPayloadSchema: () => BaseEventPayloadSchema,
45
+ BatchCascadePhaseSchema: () => BatchCascadePhaseSchema,
45
46
  BatchOperationDefinitionSchema: () => BatchOperationDefinitionSchema,
46
47
  BatchOperationIterateResultSchema: () => BatchOperationIterateResultSchema,
47
48
  BatchOperationSetupResultSchema: () => BatchOperationSetupResultSchema,
@@ -273,6 +274,7 @@ __export(index_exports, {
273
274
  computeSkewedExpectedMinorUnits: () => computeSkewedExpectedMinorUnits,
274
275
  configure: () => configure,
275
276
  createAuthError: () => createAuthError,
277
+ createBatchOperationContext: () => createBatchOperationContext,
276
278
  createConflictError: () => createConflictError,
277
279
  createContextLogger: () => createContextLogger,
278
280
  createErrorResponse: () => createErrorResponse,
@@ -420,6 +422,17 @@ function createWorkflowStepContext(params) {
420
422
  workflowStepId: params.workflowStepId
421
423
  };
422
424
  }
425
+ function createBatchOperationContext(params) {
426
+ return {
427
+ invocationId: params.invocationId,
428
+ invocationType: "batch_operation",
429
+ batchOperationHandle: params.batchOperationHandle,
430
+ batchOperationMethod: params.batchOperationMethod,
431
+ appInstallationId: params.appInstallationId,
432
+ workflowId: params.workflowId,
433
+ workflowRunId: params.workflowRunId
434
+ };
435
+ }
423
436
 
424
437
  // src/types/tool-context.ts
425
438
  function isProvisionContext(ctx) {
@@ -2291,8 +2304,15 @@ var BatchOperationSetupResultSchema = import_v48.z.object({
2291
2304
  state: import_v48.z.record(import_v48.z.string(), import_v48.z.unknown()).optional(),
2292
2305
  total: import_v48.z.number().int().nonnegative().optional()
2293
2306
  });
2307
+ var BatchCascadePhaseSchema = import_v48.z.object({
2308
+ entity: import_v48.z.string(),
2309
+ order: import_v48.z.number().int(),
2310
+ wave: import_v48.z.enum(["setup", "page"]),
2311
+ requires: import_v48.z.array(import_v48.z.string()).optional()
2312
+ });
2294
2313
  var BatchOperationIterateResultSchema = import_v48.z.object({
2295
2314
  items: import_v48.z.array(import_v48.z.record(import_v48.z.string(), import_v48.z.unknown())),
2315
+ itemsByEntity: import_v48.z.record(import_v48.z.string(), import_v48.z.array(import_v48.z.record(import_v48.z.string(), import_v48.z.unknown()))).optional(),
2296
2316
  pagination: BatchPaginationSchema,
2297
2317
  state: import_v48.z.record(import_v48.z.string(), import_v48.z.unknown()).optional()
2298
2318
  });
@@ -2301,6 +2321,7 @@ var BatchOperationDefinitionSchema = import_v48.z.object({
2301
2321
  label: import_v48.z.string(),
2302
2322
  description: import_v48.z.string().optional(),
2303
2323
  entity: import_v48.z.string(),
2324
+ cascade: import_v48.z.array(BatchCascadePhaseSchema).optional(),
2304
2325
  maxConcurrent: import_v48.z.number().int().positive().optional(),
2305
2326
  pageSize: import_v48.z.number().int().positive().optional(),
2306
2327
  icon: import_v48.z.string().optional()
@@ -5843,6 +5864,39 @@ async function handleMcpBatchRoute(req, ctx) {
5843
5864
  }
5844
5865
  };
5845
5866
  }
5867
+ function isBatchOperationFailure(result) {
5868
+ return result != null && typeof result === "object" && result.success === false && typeof result.error === "object" && result.error != null;
5869
+ }
5870
+ function toStringEnv(env) {
5871
+ const out = {};
5872
+ for (const [key, value] of Object.entries(env)) {
5873
+ if (value !== void 0) {
5874
+ out[key] = value;
5875
+ }
5876
+ }
5877
+ return out;
5878
+ }
5879
+ function stripBillingFields(result) {
5880
+ const {
5881
+ billing: _billing,
5882
+ success: _success,
5883
+ error: _error,
5884
+ retry: _retry,
5885
+ ...domain
5886
+ } = result;
5887
+ return domain;
5888
+ }
5889
+ function softFailureResponse(error, retry, billing) {
5890
+ return {
5891
+ status: 200,
5892
+ body: {
5893
+ success: false,
5894
+ error,
5895
+ ...retry ? { retry } : {},
5896
+ billing: normalizeBilling(billing)
5897
+ }
5898
+ };
5899
+ }
5846
5900
  async function handleBatchOperationRoute(req, ctx) {
5847
5901
  const bodyResult = parseJsonBody(req);
5848
5902
  if (!bodyResult.success) {
@@ -5851,49 +5905,80 @@ async function handleBatchOperationRoute(req, ctx) {
5851
5905
  const body = bodyResult.data;
5852
5906
  const registry = ctx.batchOperationRegistry;
5853
5907
  if (!registry) {
5854
- return {
5855
- status: 404,
5856
- body: {
5857
- success: false,
5858
- error: "No batch operations registered"
5859
- }
5860
- };
5908
+ return softFailureResponse({
5909
+ code: "NOT_FOUND",
5910
+ message: "No batch operations registered",
5911
+ category: "validation"
5912
+ });
5861
5913
  }
5862
5914
  const operation = registry[body.handle];
5863
5915
  if (!operation) {
5864
- return {
5865
- status: 404,
5866
- body: {
5867
- success: false,
5868
- error: `Batch operation not found: ${body.handle}`
5869
- }
5870
- };
5916
+ return softFailureResponse({
5917
+ code: "NOT_FOUND",
5918
+ message: `Batch operation not found: ${body.handle}`,
5919
+ category: "validation"
5920
+ });
5871
5921
  }
5872
5922
  const context = body.context;
5873
5923
  if (!context?.workplaceId || !context?.appInstallationId || !context?.appId) {
5874
- return {
5875
- status: 400,
5876
- body: {
5877
- success: false,
5878
- error: "Missing context (workplaceId, appInstallationId, and appId required)"
5879
- }
5880
- };
5924
+ return softFailureResponse({
5925
+ code: "VALIDATION_ERROR",
5926
+ message: "Missing context (workplaceId, appInstallationId, and appId required)",
5927
+ category: "validation"
5928
+ });
5881
5929
  }
5882
- const env = body.env ?? {};
5883
- const log = {
5884
- info: (message, meta) => {
5885
- console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
5886
- },
5887
- warn: (message, meta) => {
5888
- console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
5889
- },
5890
- error: (message, meta) => {
5891
- console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
5892
- }
5930
+ if (body.method !== "setup" && body.method !== "iterate") {
5931
+ return softFailureResponse({
5932
+ code: "VALIDATION_ERROR",
5933
+ message: `Unknown method: ${body.method}`,
5934
+ category: "validation"
5935
+ });
5936
+ }
5937
+ const requestEnv = body.env ?? {};
5938
+ const mergedEnv = toStringEnv(buildToolExecutionEnv(requestEnv));
5939
+ const originalEnv = { ...process.env };
5940
+ Object.assign(process.env, mergedEnv);
5941
+ const log = createContextLogger();
5942
+ const app = context.app ?? {
5943
+ id: context.appId,
5944
+ versionId: context.appId
5945
+ };
5946
+ const workplace2 = context.workplace ?? {
5947
+ id: context.workplaceId,
5948
+ subdomain: ""
5949
+ };
5950
+ const invocation = body.invocation ?? {
5951
+ invocationId: `${body.handle}-${body.method}-${Date.now()}`,
5952
+ invocationType: "batch_operation",
5953
+ batchOperationHandle: body.handle,
5954
+ batchOperationMethod: body.method,
5955
+ appInstallationId: context.appInstallationId
5893
5956
  };
5894
5957
  const requestConfig = {
5895
- baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
5896
- apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
5958
+ baseUrl: mergedEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
5959
+ apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
5960
+ };
5961
+ const rateLimitContext = {
5962
+ app,
5963
+ appInstallationId: context.appInstallationId,
5964
+ invocation,
5965
+ preAcquiredLeases: parsePreAcquiredLeases(
5966
+ mergedEnv.SKEDYUL_RATE_LIMIT_LEASES
5967
+ ),
5968
+ heldMutexQueueKeys: parseHeldMutexQueueKeys(
5969
+ mergedEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
5970
+ )
5971
+ };
5972
+ const opContextBase = {
5973
+ workplaceId: context.workplaceId,
5974
+ appInstallationId: context.appInstallationId,
5975
+ appId: context.appId,
5976
+ app,
5977
+ workplace: workplace2,
5978
+ input: body.input,
5979
+ env: mergedEnv,
5980
+ invocation,
5981
+ log
5897
5982
  };
5898
5983
  try {
5899
5984
  if (body.method === "setup") {
@@ -5902,67 +5987,98 @@ async function handleBatchOperationRoute(req, ctx) {
5902
5987
  status: 200,
5903
5988
  body: {
5904
5989
  success: true,
5905
- result: { state: {}, total: void 0 }
5990
+ result: {
5991
+ state: {},
5992
+ total: void 0,
5993
+ cascade: operation.cascade
5994
+ },
5995
+ billing: normalizeBilling(void 0)
5906
5996
  }
5907
5997
  };
5908
5998
  }
5909
- const result = await runWithConfig(requestConfig, async () => {
5910
- return operation.setup({
5911
- workplaceId: context.workplaceId,
5912
- appInstallationId: context.appInstallationId,
5913
- appId: context.appId,
5914
- input: body.input,
5915
- env,
5916
- log
5999
+ const raw2 = await runWithConfig(requestConfig, async () => {
6000
+ return runWithRateLimitExecutionContext(rateLimitContext, async () => {
6001
+ return runWithLogContext({ invocation }, async () => {
6002
+ return operation.setup(opContextBase);
6003
+ });
5917
6004
  });
5918
6005
  });
6006
+ if (isBatchOperationFailure(raw2)) {
6007
+ return softFailureResponse(raw2.error, raw2.retry, raw2.billing);
6008
+ }
6009
+ const setupResult = raw2;
6010
+ const domain2 = stripBillingFields(
6011
+ setupResult
6012
+ );
5919
6013
  return {
5920
6014
  status: 200,
5921
6015
  body: {
5922
6016
  success: true,
5923
- result
6017
+ result: {
6018
+ ...domain2,
6019
+ // Expose definition cascade to the platform workflow
6020
+ cascade: operation.cascade ?? domain2.cascade
6021
+ },
6022
+ billing: normalizeBilling(setupResult.billing)
5924
6023
  }
5925
6024
  };
5926
6025
  }
5927
- if (body.method === "iterate") {
5928
- const result = await runWithConfig(requestConfig, async () => {
5929
- return operation.iterate({
5930
- workplaceId: context.workplaceId,
5931
- appInstallationId: context.appInstallationId,
5932
- appId: context.appId,
5933
- input: body.input,
5934
- state: body.state,
5935
- page: body.page,
5936
- cursor: body.cursor,
5937
- limit: body.limit,
5938
- env,
5939
- log
6026
+ const iterateBody = body;
6027
+ const raw = await runWithConfig(requestConfig, async () => {
6028
+ return runWithRateLimitExecutionContext(rateLimitContext, async () => {
6029
+ return runWithLogContext({ invocation }, async () => {
6030
+ return operation.iterate({
6031
+ ...opContextBase,
6032
+ state: iterateBody.state,
6033
+ page: iterateBody.page,
6034
+ cursor: iterateBody.cursor,
6035
+ limit: iterateBody.limit,
6036
+ cascadeEntities: iterateBody.cascadeEntities
6037
+ });
5940
6038
  });
5941
6039
  });
5942
- return {
5943
- status: 200,
5944
- body: {
5945
- success: true,
5946
- result
5947
- }
5948
- };
6040
+ });
6041
+ if (isBatchOperationFailure(raw)) {
6042
+ return softFailureResponse(raw.error, raw.retry, raw.billing);
5949
6043
  }
6044
+ const iterateResult = raw;
6045
+ const domain = stripBillingFields(
6046
+ iterateResult
6047
+ );
5950
6048
  return {
5951
- status: 400,
6049
+ status: 200,
5952
6050
  body: {
5953
- success: false,
5954
- error: `Unknown method: ${body.method}`
6051
+ success: true,
6052
+ result: domain,
6053
+ billing: normalizeBilling(iterateResult.billing)
5955
6054
  }
5956
6055
  };
5957
6056
  } catch (err) {
5958
- log.error("Batch operation failed", { error: err });
6057
+ if (err instanceof RateLimitExceededError) {
6058
+ return softFailureResponse(
6059
+ {
6060
+ code: "RATE_LIMITED",
6061
+ message: err.message,
6062
+ category: "external"
6063
+ },
6064
+ { allowed: true, afterMs: err.retryAfterMs }
6065
+ );
6066
+ }
6067
+ log.error("Batch operation failed", err);
5959
6068
  return {
5960
6069
  status: 500,
5961
6070
  body: {
5962
6071
  success: false,
5963
- error: err instanceof Error ? err.message : String(err)
6072
+ error: {
6073
+ code: "INTERNAL_ERROR",
6074
+ message: err instanceof Error ? err.message : String(err),
6075
+ category: "internal"
6076
+ },
6077
+ billing: normalizeBilling(void 0)
5964
6078
  }
5965
6079
  };
6080
+ } finally {
6081
+ process.env = originalEnv;
5966
6082
  }
5967
6083
  }
5968
6084
  function createNotFoundResponse() {
@@ -8962,6 +9078,7 @@ var index_default = { z: import_v414.z };
8962
9078
  AssociationSchema,
8963
9079
  AuthenticationError,
8964
9080
  BaseEventPayloadSchema,
9081
+ BatchCascadePhaseSchema,
8965
9082
  BatchOperationDefinitionSchema,
8966
9083
  BatchOperationIterateResultSchema,
8967
9084
  BatchOperationSetupResultSchema,
@@ -9193,6 +9310,7 @@ var index_default = { z: import_v414.z };
9193
9310
  computeSkewedExpectedMinorUnits,
9194
9311
  configure,
9195
9312
  createAuthError,
9313
+ createBatchOperationContext,
9196
9314
  createConflictError,
9197
9315
  createContextLogger,
9198
9316
  createErrorResponse,
package/dist/schemas.d.ts CHANGED
@@ -3368,8 +3368,18 @@ export declare const BatchOperationSetupResultSchema: z.ZodObject<{
3368
3368
  state: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
3369
3369
  total: z.ZodOptional<z.ZodNumber>;
3370
3370
  }, z.core.$strip>;
3371
+ export declare const BatchCascadePhaseSchema: z.ZodObject<{
3372
+ entity: z.ZodString;
3373
+ order: z.ZodNumber;
3374
+ wave: z.ZodEnum<{
3375
+ page: "page";
3376
+ setup: "setup";
3377
+ }>;
3378
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
3379
+ }, z.core.$strip>;
3371
3380
  export declare const BatchOperationIterateResultSchema: z.ZodObject<{
3372
3381
  items: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
3382
+ itemsByEntity: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
3373
3383
  pagination: z.ZodObject<{
3374
3384
  hasMore: z.ZodBoolean;
3375
3385
  page: z.ZodOptional<z.ZodNumber>;
@@ -3384,6 +3394,15 @@ export declare const BatchOperationDefinitionSchema: z.ZodObject<{
3384
3394
  label: z.ZodString;
3385
3395
  description: z.ZodOptional<z.ZodString>;
3386
3396
  entity: z.ZodString;
3397
+ cascade: z.ZodOptional<z.ZodArray<z.ZodObject<{
3398
+ entity: z.ZodString;
3399
+ order: z.ZodNumber;
3400
+ wave: z.ZodEnum<{
3401
+ page: "page";
3402
+ setup: "setup";
3403
+ }>;
3404
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
3405
+ }, z.core.$strip>>>;
3387
3406
  maxConcurrent: z.ZodOptional<z.ZodNumber>;
3388
3407
  pageSize: z.ZodOptional<z.ZodNumber>;
3389
3408
  icon: z.ZodOptional<z.ZodString>;
@@ -4197,6 +4216,15 @@ export declare const ProvisionConfigSchema: z.ZodObject<{
4197
4216
  label: z.ZodString;
4198
4217
  description: z.ZodOptional<z.ZodString>;
4199
4218
  entity: z.ZodString;
4219
+ cascade: z.ZodOptional<z.ZodArray<z.ZodObject<{
4220
+ entity: z.ZodString;
4221
+ order: z.ZodNumber;
4222
+ wave: z.ZodEnum<{
4223
+ page: "page";
4224
+ setup: "setup";
4225
+ }>;
4226
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
4227
+ }, z.core.$strip>>>;
4200
4228
  maxConcurrent: z.ZodOptional<z.ZodNumber>;
4201
4229
  pageSize: z.ZodOptional<z.ZodNumber>;
4202
4230
  icon: z.ZodOptional<z.ZodString>;
@@ -5021,6 +5049,15 @@ export declare const SkedyulConfigSchema: z.ZodObject<{
5021
5049
  label: z.ZodString;
5022
5050
  description: z.ZodOptional<z.ZodString>;
5023
5051
  entity: z.ZodString;
5052
+ cascade: z.ZodOptional<z.ZodArray<z.ZodObject<{
5053
+ entity: z.ZodString;
5054
+ order: z.ZodNumber;
5055
+ wave: z.ZodEnum<{
5056
+ page: "page";
5057
+ setup: "setup";
5058
+ }>;
5059
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
5060
+ }, z.core.$strip>>>;
5024
5061
  maxConcurrent: z.ZodOptional<z.ZodNumber>;
5025
5062
  pageSize: z.ZodOptional<z.ZodNumber>;
5026
5063
  icon: z.ZodOptional<z.ZodString>;
@@ -63,8 +63,8 @@ export declare function handleMcpBatchRoute(req: UnifiedRequest, ctx: RouteConte
63
63
  /**
64
64
  * Handle /batch-operation route for invoking batch operation setup/iterate.
65
65
  *
66
- * Mirrors tool/install handlers: platform passes env + context in the body;
67
- * we wrap handlers in runWithConfig so SDK clients (instance.*, etc.) work.
66
+ * Mirrors tool handlers: buildToolExecutionEnv, process.env assign/restore,
67
+ * runWithConfig + rate-limit + log ALS wrappers, tool-shaped billing/error envelope.
68
68
  */
69
69
  export declare function handleBatchOperationRoute(req: UnifiedRequest, ctx: RouteContext): Promise<UnifiedResponse>;
70
70
  /**