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/.build-stamp +1 -1
- package/dist/cli/index.js +164 -69
- package/dist/config/types/batch-operation.d.ts +77 -6
- package/dist/dedicated/server.js +164 -69
- package/dist/esm/index.mjs +185 -69
- package/dist/index.d.ts +1 -1
- package/dist/index.js +187 -69
- package/dist/schemas.d.ts +37 -0
- package/dist/server/route-handlers/handlers.d.ts +2 -2
- package/dist/server.js +164 -69
- package/dist/serverless/server.mjs +164 -69
- package/dist/types/batch-operation.d.ts +6 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/types/invocation.d.ts +16 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -1988,6 +1988,39 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
1988
1988
|
}
|
|
1989
1989
|
};
|
|
1990
1990
|
}
|
|
1991
|
+
function isBatchOperationFailure(result) {
|
|
1992
|
+
return result != null && typeof result === "object" && result.success === false && typeof result.error === "object" && result.error != null;
|
|
1993
|
+
}
|
|
1994
|
+
function toStringEnv(env) {
|
|
1995
|
+
const out = {};
|
|
1996
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1997
|
+
if (value !== void 0) {
|
|
1998
|
+
out[key] = value;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
return out;
|
|
2002
|
+
}
|
|
2003
|
+
function stripBillingFields(result) {
|
|
2004
|
+
const {
|
|
2005
|
+
billing: _billing,
|
|
2006
|
+
success: _success,
|
|
2007
|
+
error: _error,
|
|
2008
|
+
retry: _retry,
|
|
2009
|
+
...domain
|
|
2010
|
+
} = result;
|
|
2011
|
+
return domain;
|
|
2012
|
+
}
|
|
2013
|
+
function softFailureResponse(error, retry, billing) {
|
|
2014
|
+
return {
|
|
2015
|
+
status: 200,
|
|
2016
|
+
body: {
|
|
2017
|
+
success: false,
|
|
2018
|
+
error,
|
|
2019
|
+
...retry ? { retry } : {},
|
|
2020
|
+
billing: normalizeBilling(billing)
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
1991
2024
|
async function handleBatchOperationRoute(req, ctx) {
|
|
1992
2025
|
const bodyResult = parseJsonBody(req);
|
|
1993
2026
|
if (!bodyResult.success) {
|
|
@@ -1996,49 +2029,80 @@ async function handleBatchOperationRoute(req, ctx) {
|
|
|
1996
2029
|
const body = bodyResult.data;
|
|
1997
2030
|
const registry = ctx.batchOperationRegistry;
|
|
1998
2031
|
if (!registry) {
|
|
1999
|
-
return {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
}
|
|
2005
|
-
};
|
|
2032
|
+
return softFailureResponse({
|
|
2033
|
+
code: "NOT_FOUND",
|
|
2034
|
+
message: "No batch operations registered",
|
|
2035
|
+
category: "validation"
|
|
2036
|
+
});
|
|
2006
2037
|
}
|
|
2007
2038
|
const operation = registry[body.handle];
|
|
2008
2039
|
if (!operation) {
|
|
2009
|
-
return {
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
}
|
|
2015
|
-
};
|
|
2040
|
+
return softFailureResponse({
|
|
2041
|
+
code: "NOT_FOUND",
|
|
2042
|
+
message: `Batch operation not found: ${body.handle}`,
|
|
2043
|
+
category: "validation"
|
|
2044
|
+
});
|
|
2016
2045
|
}
|
|
2017
2046
|
const context = body.context;
|
|
2018
2047
|
if (!context?.workplaceId || !context?.appInstallationId || !context?.appId) {
|
|
2019
|
-
return {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
}
|
|
2025
|
-
};
|
|
2048
|
+
return softFailureResponse({
|
|
2049
|
+
code: "VALIDATION_ERROR",
|
|
2050
|
+
message: "Missing context (workplaceId, appInstallationId, and appId required)",
|
|
2051
|
+
category: "validation"
|
|
2052
|
+
});
|
|
2026
2053
|
}
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2054
|
+
if (body.method !== "setup" && body.method !== "iterate") {
|
|
2055
|
+
return softFailureResponse({
|
|
2056
|
+
code: "VALIDATION_ERROR",
|
|
2057
|
+
message: `Unknown method: ${body.method}`,
|
|
2058
|
+
category: "validation"
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
const requestEnv = body.env ?? {};
|
|
2062
|
+
const mergedEnv = toStringEnv(buildToolExecutionEnv(requestEnv));
|
|
2063
|
+
const originalEnv = { ...process.env };
|
|
2064
|
+
Object.assign(process.env, mergedEnv);
|
|
2065
|
+
const log = createContextLogger();
|
|
2066
|
+
const app = context.app ?? {
|
|
2067
|
+
id: context.appId,
|
|
2068
|
+
versionId: context.appId
|
|
2069
|
+
};
|
|
2070
|
+
const workplace = context.workplace ?? {
|
|
2071
|
+
id: context.workplaceId,
|
|
2072
|
+
subdomain: ""
|
|
2073
|
+
};
|
|
2074
|
+
const invocation = body.invocation ?? {
|
|
2075
|
+
invocationId: `${body.handle}-${body.method}-${Date.now()}`,
|
|
2076
|
+
invocationType: "batch_operation",
|
|
2077
|
+
batchOperationHandle: body.handle,
|
|
2078
|
+
batchOperationMethod: body.method,
|
|
2079
|
+
appInstallationId: context.appInstallationId
|
|
2038
2080
|
};
|
|
2039
2081
|
const requestConfig = {
|
|
2040
|
-
baseUrl:
|
|
2041
|
-
apiToken:
|
|
2082
|
+
baseUrl: mergedEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
|
|
2083
|
+
apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
|
|
2084
|
+
};
|
|
2085
|
+
const rateLimitContext = {
|
|
2086
|
+
app,
|
|
2087
|
+
appInstallationId: context.appInstallationId,
|
|
2088
|
+
invocation,
|
|
2089
|
+
preAcquiredLeases: parsePreAcquiredLeases(
|
|
2090
|
+
mergedEnv.SKEDYUL_RATE_LIMIT_LEASES
|
|
2091
|
+
),
|
|
2092
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
2093
|
+
mergedEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
2094
|
+
)
|
|
2095
|
+
};
|
|
2096
|
+
const opContextBase = {
|
|
2097
|
+
workplaceId: context.workplaceId,
|
|
2098
|
+
appInstallationId: context.appInstallationId,
|
|
2099
|
+
appId: context.appId,
|
|
2100
|
+
app,
|
|
2101
|
+
workplace,
|
|
2102
|
+
input: body.input,
|
|
2103
|
+
env: mergedEnv,
|
|
2104
|
+
invocation,
|
|
2105
|
+
log
|
|
2042
2106
|
};
|
|
2043
2107
|
try {
|
|
2044
2108
|
if (body.method === "setup") {
|
|
@@ -2047,67 +2111,98 @@ async function handleBatchOperationRoute(req, ctx) {
|
|
|
2047
2111
|
status: 200,
|
|
2048
2112
|
body: {
|
|
2049
2113
|
success: true,
|
|
2050
|
-
result: {
|
|
2114
|
+
result: {
|
|
2115
|
+
state: {},
|
|
2116
|
+
total: void 0,
|
|
2117
|
+
cascade: operation.cascade
|
|
2118
|
+
},
|
|
2119
|
+
billing: normalizeBilling(void 0)
|
|
2051
2120
|
}
|
|
2052
2121
|
};
|
|
2053
2122
|
}
|
|
2054
|
-
const
|
|
2055
|
-
return
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
input: body.input,
|
|
2060
|
-
env,
|
|
2061
|
-
log
|
|
2123
|
+
const raw2 = await runWithConfig(requestConfig, async () => {
|
|
2124
|
+
return runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
2125
|
+
return runWithLogContext({ invocation }, async () => {
|
|
2126
|
+
return operation.setup(opContextBase);
|
|
2127
|
+
});
|
|
2062
2128
|
});
|
|
2063
2129
|
});
|
|
2130
|
+
if (isBatchOperationFailure(raw2)) {
|
|
2131
|
+
return softFailureResponse(raw2.error, raw2.retry, raw2.billing);
|
|
2132
|
+
}
|
|
2133
|
+
const setupResult = raw2;
|
|
2134
|
+
const domain2 = stripBillingFields(
|
|
2135
|
+
setupResult
|
|
2136
|
+
);
|
|
2064
2137
|
return {
|
|
2065
2138
|
status: 200,
|
|
2066
2139
|
body: {
|
|
2067
2140
|
success: true,
|
|
2068
|
-
result
|
|
2141
|
+
result: {
|
|
2142
|
+
...domain2,
|
|
2143
|
+
// Expose definition cascade to the platform workflow
|
|
2144
|
+
cascade: operation.cascade ?? domain2.cascade
|
|
2145
|
+
},
|
|
2146
|
+
billing: normalizeBilling(setupResult.billing)
|
|
2069
2147
|
}
|
|
2070
2148
|
};
|
|
2071
2149
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
log
|
|
2150
|
+
const iterateBody = body;
|
|
2151
|
+
const raw = await runWithConfig(requestConfig, async () => {
|
|
2152
|
+
return runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
2153
|
+
return runWithLogContext({ invocation }, async () => {
|
|
2154
|
+
return operation.iterate({
|
|
2155
|
+
...opContextBase,
|
|
2156
|
+
state: iterateBody.state,
|
|
2157
|
+
page: iterateBody.page,
|
|
2158
|
+
cursor: iterateBody.cursor,
|
|
2159
|
+
limit: iterateBody.limit,
|
|
2160
|
+
cascadeEntities: iterateBody.cascadeEntities
|
|
2161
|
+
});
|
|
2085
2162
|
});
|
|
2086
2163
|
});
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
success: true,
|
|
2091
|
-
result
|
|
2092
|
-
}
|
|
2093
|
-
};
|
|
2164
|
+
});
|
|
2165
|
+
if (isBatchOperationFailure(raw)) {
|
|
2166
|
+
return softFailureResponse(raw.error, raw.retry, raw.billing);
|
|
2094
2167
|
}
|
|
2168
|
+
const iterateResult = raw;
|
|
2169
|
+
const domain = stripBillingFields(
|
|
2170
|
+
iterateResult
|
|
2171
|
+
);
|
|
2095
2172
|
return {
|
|
2096
|
-
status:
|
|
2173
|
+
status: 200,
|
|
2097
2174
|
body: {
|
|
2098
|
-
success:
|
|
2099
|
-
|
|
2175
|
+
success: true,
|
|
2176
|
+
result: domain,
|
|
2177
|
+
billing: normalizeBilling(iterateResult.billing)
|
|
2100
2178
|
}
|
|
2101
2179
|
};
|
|
2102
2180
|
} catch (err) {
|
|
2103
|
-
|
|
2181
|
+
if (err instanceof RateLimitExceededError) {
|
|
2182
|
+
return softFailureResponse(
|
|
2183
|
+
{
|
|
2184
|
+
code: "RATE_LIMITED",
|
|
2185
|
+
message: err.message,
|
|
2186
|
+
category: "external"
|
|
2187
|
+
},
|
|
2188
|
+
{ allowed: true, afterMs: err.retryAfterMs }
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
log.error("Batch operation failed", err);
|
|
2104
2192
|
return {
|
|
2105
2193
|
status: 500,
|
|
2106
2194
|
body: {
|
|
2107
2195
|
success: false,
|
|
2108
|
-
error:
|
|
2196
|
+
error: {
|
|
2197
|
+
code: "INTERNAL_ERROR",
|
|
2198
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2199
|
+
category: "internal"
|
|
2200
|
+
},
|
|
2201
|
+
billing: normalizeBilling(void 0)
|
|
2109
2202
|
}
|
|
2110
2203
|
};
|
|
2204
|
+
} finally {
|
|
2205
|
+
process.env = originalEnv;
|
|
2111
2206
|
}
|
|
2112
2207
|
}
|
|
2113
2208
|
function createNotFoundResponse() {
|
|
@@ -1949,6 +1949,39 @@ async function handleMcpBatchRoute(req, ctx) {
|
|
|
1949
1949
|
}
|
|
1950
1950
|
};
|
|
1951
1951
|
}
|
|
1952
|
+
function isBatchOperationFailure(result) {
|
|
1953
|
+
return result != null && typeof result === "object" && result.success === false && typeof result.error === "object" && result.error != null;
|
|
1954
|
+
}
|
|
1955
|
+
function toStringEnv(env) {
|
|
1956
|
+
const out = {};
|
|
1957
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1958
|
+
if (value !== void 0) {
|
|
1959
|
+
out[key] = value;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
return out;
|
|
1963
|
+
}
|
|
1964
|
+
function stripBillingFields(result) {
|
|
1965
|
+
const {
|
|
1966
|
+
billing: _billing,
|
|
1967
|
+
success: _success,
|
|
1968
|
+
error: _error,
|
|
1969
|
+
retry: _retry,
|
|
1970
|
+
...domain
|
|
1971
|
+
} = result;
|
|
1972
|
+
return domain;
|
|
1973
|
+
}
|
|
1974
|
+
function softFailureResponse(error, retry, billing) {
|
|
1975
|
+
return {
|
|
1976
|
+
status: 200,
|
|
1977
|
+
body: {
|
|
1978
|
+
success: false,
|
|
1979
|
+
error,
|
|
1980
|
+
...retry ? { retry } : {},
|
|
1981
|
+
billing: normalizeBilling(billing)
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1952
1985
|
async function handleBatchOperationRoute(req, ctx) {
|
|
1953
1986
|
const bodyResult = parseJsonBody(req);
|
|
1954
1987
|
if (!bodyResult.success) {
|
|
@@ -1957,49 +1990,80 @@ async function handleBatchOperationRoute(req, ctx) {
|
|
|
1957
1990
|
const body = bodyResult.data;
|
|
1958
1991
|
const registry = ctx.batchOperationRegistry;
|
|
1959
1992
|
if (!registry) {
|
|
1960
|
-
return {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
}
|
|
1966
|
-
};
|
|
1993
|
+
return softFailureResponse({
|
|
1994
|
+
code: "NOT_FOUND",
|
|
1995
|
+
message: "No batch operations registered",
|
|
1996
|
+
category: "validation"
|
|
1997
|
+
});
|
|
1967
1998
|
}
|
|
1968
1999
|
const operation = registry[body.handle];
|
|
1969
2000
|
if (!operation) {
|
|
1970
|
-
return {
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
}
|
|
1976
|
-
};
|
|
2001
|
+
return softFailureResponse({
|
|
2002
|
+
code: "NOT_FOUND",
|
|
2003
|
+
message: `Batch operation not found: ${body.handle}`,
|
|
2004
|
+
category: "validation"
|
|
2005
|
+
});
|
|
1977
2006
|
}
|
|
1978
2007
|
const context = body.context;
|
|
1979
2008
|
if (!context?.workplaceId || !context?.appInstallationId || !context?.appId) {
|
|
1980
|
-
return {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
}
|
|
1986
|
-
};
|
|
2009
|
+
return softFailureResponse({
|
|
2010
|
+
code: "VALIDATION_ERROR",
|
|
2011
|
+
message: "Missing context (workplaceId, appInstallationId, and appId required)",
|
|
2012
|
+
category: "validation"
|
|
2013
|
+
});
|
|
1987
2014
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2015
|
+
if (body.method !== "setup" && body.method !== "iterate") {
|
|
2016
|
+
return softFailureResponse({
|
|
2017
|
+
code: "VALIDATION_ERROR",
|
|
2018
|
+
message: `Unknown method: ${body.method}`,
|
|
2019
|
+
category: "validation"
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
const requestEnv = body.env ?? {};
|
|
2023
|
+
const mergedEnv = toStringEnv(buildToolExecutionEnv(requestEnv));
|
|
2024
|
+
const originalEnv = { ...process.env };
|
|
2025
|
+
Object.assign(process.env, mergedEnv);
|
|
2026
|
+
const log = createContextLogger();
|
|
2027
|
+
const app = context.app ?? {
|
|
2028
|
+
id: context.appId,
|
|
2029
|
+
versionId: context.appId
|
|
2030
|
+
};
|
|
2031
|
+
const workplace = context.workplace ?? {
|
|
2032
|
+
id: context.workplaceId,
|
|
2033
|
+
subdomain: ""
|
|
2034
|
+
};
|
|
2035
|
+
const invocation = body.invocation ?? {
|
|
2036
|
+
invocationId: `${body.handle}-${body.method}-${Date.now()}`,
|
|
2037
|
+
invocationType: "batch_operation",
|
|
2038
|
+
batchOperationHandle: body.handle,
|
|
2039
|
+
batchOperationMethod: body.method,
|
|
2040
|
+
appInstallationId: context.appInstallationId
|
|
1999
2041
|
};
|
|
2000
2042
|
const requestConfig = {
|
|
2001
|
-
baseUrl:
|
|
2002
|
-
apiToken:
|
|
2043
|
+
baseUrl: mergedEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
|
|
2044
|
+
apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
|
|
2045
|
+
};
|
|
2046
|
+
const rateLimitContext = {
|
|
2047
|
+
app,
|
|
2048
|
+
appInstallationId: context.appInstallationId,
|
|
2049
|
+
invocation,
|
|
2050
|
+
preAcquiredLeases: parsePreAcquiredLeases(
|
|
2051
|
+
mergedEnv.SKEDYUL_RATE_LIMIT_LEASES
|
|
2052
|
+
),
|
|
2053
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
2054
|
+
mergedEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
2055
|
+
)
|
|
2056
|
+
};
|
|
2057
|
+
const opContextBase = {
|
|
2058
|
+
workplaceId: context.workplaceId,
|
|
2059
|
+
appInstallationId: context.appInstallationId,
|
|
2060
|
+
appId: context.appId,
|
|
2061
|
+
app,
|
|
2062
|
+
workplace,
|
|
2063
|
+
input: body.input,
|
|
2064
|
+
env: mergedEnv,
|
|
2065
|
+
invocation,
|
|
2066
|
+
log
|
|
2003
2067
|
};
|
|
2004
2068
|
try {
|
|
2005
2069
|
if (body.method === "setup") {
|
|
@@ -2008,67 +2072,98 @@ async function handleBatchOperationRoute(req, ctx) {
|
|
|
2008
2072
|
status: 200,
|
|
2009
2073
|
body: {
|
|
2010
2074
|
success: true,
|
|
2011
|
-
result: {
|
|
2075
|
+
result: {
|
|
2076
|
+
state: {},
|
|
2077
|
+
total: void 0,
|
|
2078
|
+
cascade: operation.cascade
|
|
2079
|
+
},
|
|
2080
|
+
billing: normalizeBilling(void 0)
|
|
2012
2081
|
}
|
|
2013
2082
|
};
|
|
2014
2083
|
}
|
|
2015
|
-
const
|
|
2016
|
-
return
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
input: body.input,
|
|
2021
|
-
env,
|
|
2022
|
-
log
|
|
2084
|
+
const raw2 = await runWithConfig(requestConfig, async () => {
|
|
2085
|
+
return runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
2086
|
+
return runWithLogContext({ invocation }, async () => {
|
|
2087
|
+
return operation.setup(opContextBase);
|
|
2088
|
+
});
|
|
2023
2089
|
});
|
|
2024
2090
|
});
|
|
2091
|
+
if (isBatchOperationFailure(raw2)) {
|
|
2092
|
+
return softFailureResponse(raw2.error, raw2.retry, raw2.billing);
|
|
2093
|
+
}
|
|
2094
|
+
const setupResult = raw2;
|
|
2095
|
+
const domain2 = stripBillingFields(
|
|
2096
|
+
setupResult
|
|
2097
|
+
);
|
|
2025
2098
|
return {
|
|
2026
2099
|
status: 200,
|
|
2027
2100
|
body: {
|
|
2028
2101
|
success: true,
|
|
2029
|
-
result
|
|
2102
|
+
result: {
|
|
2103
|
+
...domain2,
|
|
2104
|
+
// Expose definition cascade to the platform workflow
|
|
2105
|
+
cascade: operation.cascade ?? domain2.cascade
|
|
2106
|
+
},
|
|
2107
|
+
billing: normalizeBilling(setupResult.billing)
|
|
2030
2108
|
}
|
|
2031
2109
|
};
|
|
2032
2110
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
log
|
|
2111
|
+
const iterateBody = body;
|
|
2112
|
+
const raw = await runWithConfig(requestConfig, async () => {
|
|
2113
|
+
return runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
2114
|
+
return runWithLogContext({ invocation }, async () => {
|
|
2115
|
+
return operation.iterate({
|
|
2116
|
+
...opContextBase,
|
|
2117
|
+
state: iterateBody.state,
|
|
2118
|
+
page: iterateBody.page,
|
|
2119
|
+
cursor: iterateBody.cursor,
|
|
2120
|
+
limit: iterateBody.limit,
|
|
2121
|
+
cascadeEntities: iterateBody.cascadeEntities
|
|
2122
|
+
});
|
|
2046
2123
|
});
|
|
2047
2124
|
});
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
success: true,
|
|
2052
|
-
result
|
|
2053
|
-
}
|
|
2054
|
-
};
|
|
2125
|
+
});
|
|
2126
|
+
if (isBatchOperationFailure(raw)) {
|
|
2127
|
+
return softFailureResponse(raw.error, raw.retry, raw.billing);
|
|
2055
2128
|
}
|
|
2129
|
+
const iterateResult = raw;
|
|
2130
|
+
const domain = stripBillingFields(
|
|
2131
|
+
iterateResult
|
|
2132
|
+
);
|
|
2056
2133
|
return {
|
|
2057
|
-
status:
|
|
2134
|
+
status: 200,
|
|
2058
2135
|
body: {
|
|
2059
|
-
success:
|
|
2060
|
-
|
|
2136
|
+
success: true,
|
|
2137
|
+
result: domain,
|
|
2138
|
+
billing: normalizeBilling(iterateResult.billing)
|
|
2061
2139
|
}
|
|
2062
2140
|
};
|
|
2063
2141
|
} catch (err) {
|
|
2064
|
-
|
|
2142
|
+
if (err instanceof RateLimitExceededError) {
|
|
2143
|
+
return softFailureResponse(
|
|
2144
|
+
{
|
|
2145
|
+
code: "RATE_LIMITED",
|
|
2146
|
+
message: err.message,
|
|
2147
|
+
category: "external"
|
|
2148
|
+
},
|
|
2149
|
+
{ allowed: true, afterMs: err.retryAfterMs }
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
log.error("Batch operation failed", err);
|
|
2065
2153
|
return {
|
|
2066
2154
|
status: 500,
|
|
2067
2155
|
body: {
|
|
2068
2156
|
success: false,
|
|
2069
|
-
error:
|
|
2157
|
+
error: {
|
|
2158
|
+
code: "INTERNAL_ERROR",
|
|
2159
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2160
|
+
category: "internal"
|
|
2161
|
+
},
|
|
2162
|
+
billing: normalizeBilling(void 0)
|
|
2070
2163
|
}
|
|
2071
2164
|
};
|
|
2165
|
+
} finally {
|
|
2166
|
+
process.env = originalEnv;
|
|
2072
2167
|
}
|
|
2073
2168
|
}
|
|
2074
2169
|
function createNotFoundResponse() {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* These types define the registry and context for batch operations at runtime.
|
|
5
5
|
*/
|
|
6
|
-
import type { BatchOperationSetupFn, BatchOperationIterateFn } from '../config/types/batch-operation';
|
|
7
|
-
export type { BatchOperationContext } from '../config/types/batch-operation';
|
|
6
|
+
import type { BatchCascadePhase, BatchOperationSetupFn, BatchOperationIterateFn } from '../config/types/batch-operation';
|
|
7
|
+
export type { BatchCascadePhase, BatchOperationContext, } from '../config/types/batch-operation';
|
|
8
8
|
/**
|
|
9
9
|
* Entry in the batch operation registry.
|
|
10
10
|
* Contains both metadata and function implementations.
|
|
@@ -16,8 +16,10 @@ export interface BatchOperationRegistryEntry {
|
|
|
16
16
|
label: string;
|
|
17
17
|
/** Description of what this operation does */
|
|
18
18
|
description?: string;
|
|
19
|
-
/**
|
|
19
|
+
/** Primary entity handle that items will be upserted to */
|
|
20
20
|
entity: string;
|
|
21
|
+
/** Optional multi-entity cascade phases */
|
|
22
|
+
cascade?: BatchCascadePhase[];
|
|
21
23
|
/** Setup function called once at the start */
|
|
22
24
|
setup?: BatchOperationSetupFn;
|
|
23
25
|
/** Iterate function called for each page */
|
|
@@ -41,6 +43,7 @@ export interface BatchOperationMetadata {
|
|
|
41
43
|
label: string;
|
|
42
44
|
description?: string;
|
|
43
45
|
entity: string;
|
|
46
|
+
cascade?: BatchCascadePhase[];
|
|
44
47
|
maxConcurrent?: number;
|
|
45
48
|
pageSize?: number;
|
|
46
49
|
icon?: string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export type { AppInfo, AppInfoWithHandles, WorkplaceInfo, RequestInfo } from './shared';
|
|
8
8
|
export type { InvocationType, ServerHookHandle, InvocationContext, } from './invocation';
|
|
9
|
-
export { createToolCallContext, createServerHookContext, createWebhookContext, createWorkflowStepContext, } from './invocation';
|
|
9
|
+
export { createToolCallContext, createServerHookContext, createWebhookContext, createWorkflowStepContext, createBatchOperationContext, } from './invocation';
|
|
10
10
|
export type { ToolTrigger, ProvisionToolContext, DeveloperPageActionToolContext, DeveloperFormSubmitToolContext, FieldChangeToolContext, PageActionToolContext, FormSubmitToolContext, AgentToolContext, WorkflowToolContext, CronToolContext, CronContext, ToolExecutionContext, } from './tool-context';
|
|
11
11
|
export { isProvisionContext, isRuntimeContext, isCronContext, isDeveloperContext } from './tool-context';
|
|
12
12
|
export type { ToolResult, ToolSuccess, ToolFailure, ErrorCode, ErrorCategory, ToolWarning, ToolPagination, ToolBilling, ToolRetry, ToolTiming, ToolCompletionHints, ToolConfig, QueueTouchPoint, BillingInfo, ToolResponseMeta, ToolEffect, ToolError, ToolExecutionResult, ToolSchemaWithJson, ToolSchema, ToolHandler, ToolDefinition, ToolRegistryEntry, ToolRegistry, ToolName, ToolMetadata, ToolCallResponse, ExecutionScope, } from './tool';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type of invocation that triggered the executable
|
|
3
3
|
*/
|
|
4
|
-
export type InvocationType = 'tool_call' | 'server_hook' | 'workflow_step' | 'webhook';
|
|
4
|
+
export type InvocationType = 'tool_call' | 'server_hook' | 'workflow_step' | 'webhook' | 'batch_operation';
|
|
5
5
|
/**
|
|
6
6
|
* Server hook handles for lifecycle events
|
|
7
7
|
*/
|
|
@@ -19,6 +19,10 @@ export interface InvocationContext {
|
|
|
19
19
|
toolCallId?: string;
|
|
20
20
|
/** The tool's handle for searching (e.g., "get_customers") */
|
|
21
21
|
toolHandle?: string;
|
|
22
|
+
/** Batch operation handle (for batch_operation invocations) */
|
|
23
|
+
batchOperationHandle?: string;
|
|
24
|
+
/** setup | iterate */
|
|
25
|
+
batchOperationMethod?: 'setup' | 'iterate';
|
|
22
26
|
/** Hook type: "provision", "install", "uninstall", "oauth_callback", "setup.revalidate" */
|
|
23
27
|
serverHookHandle?: ServerHookHandle;
|
|
24
28
|
/** The app installation that triggered the call */
|
|
@@ -69,3 +73,14 @@ export declare function createWorkflowStepContext(params: {
|
|
|
69
73
|
workflowRunId: string;
|
|
70
74
|
workflowStepId?: string;
|
|
71
75
|
}): InvocationContext;
|
|
76
|
+
/**
|
|
77
|
+
* Create an invocation context for a batch operation setup/iterate call
|
|
78
|
+
*/
|
|
79
|
+
export declare function createBatchOperationContext(params: {
|
|
80
|
+
invocationId: string;
|
|
81
|
+
batchOperationHandle: string;
|
|
82
|
+
batchOperationMethod: 'setup' | 'iterate';
|
|
83
|
+
appInstallationId?: string;
|
|
84
|
+
workflowId?: string;
|
|
85
|
+
workflowRunId?: string;
|
|
86
|
+
}): InvocationContext;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skedyul",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.11",
|
|
4
4
|
"description": "The Skedyul SDK for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"build": "rm -rf dist tsconfig.tsbuildinfo && tsc --emitDeclarationOnly && pnpm exec tsup-node && node -e \"require('fs').mkdirSync('dist',{recursive:true}); require('fs').writeFileSync('dist/.build-stamp', String(Date.now()))\"",
|
|
77
77
|
"db:generate": "echo 'No database generation needed for this package'",
|
|
78
78
|
"dev:watch": "npx nodemon --watch src --ext js,ts --exec \"pnpm run build\"",
|
|
79
|
-
"test": "tsc --project tsconfig.tests.json && node --test dist-tests/tests/server.test.js dist-tests/tests/cli.test.js dist-tests/tests/agent-schema-v3.test.js dist-tests/tests/ratelimit.test.js dist-tests/tests/sequencer.test.js dist-tests/tests/config-loader.test.js dist-tests/tests/gsm7.test.js"
|
|
79
|
+
"test": "tsc --project tsconfig.tests.json && node --test dist-tests/tests/server.test.js dist-tests/tests/cli.test.js dist-tests/tests/agent-schema-v3.test.js dist-tests/tests/ratelimit.test.js dist-tests/tests/sequencer.test.js dist-tests/tests/config-loader.test.js dist-tests/tests/gsm7.test.js dist-tests/tests/batch-operation.test.js"
|
|
80
80
|
},
|
|
81
81
|
"keywords": [
|
|
82
82
|
"mcp",
|