@nordsym/apiclaw 1.7.2 → 1.7.4
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/convex/_generated/api.d.ts +115 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +60 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/adminActivate.d.ts +3 -0
- package/convex/adminActivate.d.ts.map +1 -0
- package/convex/adminActivate.js +47 -0
- package/convex/adminActivate.js.map +1 -0
- package/convex/adminActivate.ts +54 -0
- package/convex/adminStats.d.ts +3 -0
- package/convex/adminStats.d.ts.map +1 -0
- package/convex/adminStats.js +42 -0
- package/convex/adminStats.js.map +1 -0
- package/convex/adminStats.ts +44 -0
- package/convex/agents.d.ts +76 -0
- package/convex/agents.d.ts.map +1 -0
- package/convex/agents.js +699 -0
- package/convex/agents.js.map +1 -0
- package/convex/agents.ts +814 -0
- package/convex/analytics.d.ts +5 -0
- package/convex/analytics.d.ts.map +1 -0
- package/convex/analytics.js +166 -0
- package/convex/analytics.js.map +1 -0
- package/convex/analytics.ts +186 -0
- package/convex/billing.d.ts +88 -0
- package/convex/billing.d.ts.map +1 -0
- package/convex/billing.js +655 -0
- package/convex/billing.js.map +1 -0
- package/convex/billing.ts +791 -0
- package/convex/capabilities.d.ts +9 -0
- package/convex/capabilities.d.ts.map +1 -0
- package/convex/capabilities.js +145 -0
- package/convex/capabilities.js.map +1 -0
- package/convex/capabilities.ts +157 -0
- package/convex/chains.d.ts +68 -0
- package/convex/chains.d.ts.map +1 -0
- package/convex/chains.js +1105 -0
- package/convex/chains.js.map +1 -0
- package/convex/chains.ts +1318 -0
- package/convex/credits.d.ts +25 -0
- package/convex/credits.d.ts.map +1 -0
- package/convex/credits.js +186 -0
- package/convex/credits.js.map +1 -0
- package/convex/credits.ts +211 -0
- package/convex/crons.d.ts +3 -0
- package/convex/crons.d.ts.map +1 -0
- package/convex/crons.js +17 -0
- package/convex/crons.js.map +1 -0
- package/convex/crons.ts +28 -0
- package/convex/directCall.d.ts +72 -0
- package/convex/directCall.d.ts.map +1 -0
- package/convex/directCall.js +627 -0
- package/convex/directCall.js.map +1 -0
- package/convex/directCall.ts +678 -0
- package/convex/earnProgress.d.ts +58 -0
- package/convex/earnProgress.d.ts.map +1 -0
- package/convex/earnProgress.js +649 -0
- package/convex/earnProgress.js.map +1 -0
- package/convex/earnProgress.ts +753 -0
- package/convex/email.d.ts +14 -0
- package/convex/email.d.ts.map +1 -0
- package/convex/email.js +300 -0
- package/convex/email.js.map +1 -0
- package/convex/email.ts +329 -0
- package/convex/feedback.d.ts +7 -0
- package/convex/feedback.d.ts.map +1 -0
- package/convex/feedback.js +227 -0
- package/convex/feedback.js.map +1 -0
- package/convex/feedback.ts +265 -0
- package/convex/http.d.ts +3 -0
- package/convex/http.d.ts.map +1 -0
- package/convex/http.js +1405 -0
- package/convex/http.js.map +1 -0
- package/convex/http.ts +1577 -0
- package/convex/inbound.d.ts +2 -0
- package/convex/inbound.d.ts.map +1 -0
- package/convex/inbound.js +32 -0
- package/convex/inbound.js.map +1 -0
- package/convex/inbound.ts +32 -0
- package/convex/logs.d.ts +38 -0
- package/convex/logs.d.ts.map +1 -0
- package/convex/logs.js +487 -0
- package/convex/logs.js.map +1 -0
- package/convex/logs.ts +550 -0
- package/convex/mou.d.ts +6 -0
- package/convex/mou.d.ts.map +1 -0
- package/convex/mou.js +82 -0
- package/convex/mou.js.map +1 -0
- package/convex/mou.ts +91 -0
- package/convex/providerKeys.d.ts +31 -0
- package/convex/providerKeys.d.ts.map +1 -0
- package/convex/providerKeys.js +257 -0
- package/convex/providerKeys.js.map +1 -0
- package/convex/providerKeys.ts +289 -0
- package/convex/providers.d.ts +32 -0
- package/convex/providers.d.ts.map +1 -0
- package/convex/providers.js +814 -0
- package/convex/providers.js.map +1 -0
- package/convex/providers.ts +909 -0
- package/convex/purchases.d.ts +7 -0
- package/convex/purchases.d.ts.map +1 -0
- package/convex/purchases.js +157 -0
- package/convex/purchases.js.map +1 -0
- package/convex/purchases.ts +183 -0
- package/convex/ratelimit.d.ts +4 -0
- package/convex/ratelimit.d.ts.map +1 -0
- package/convex/ratelimit.js +91 -0
- package/convex/ratelimit.js.map +1 -0
- package/convex/ratelimit.ts +104 -0
- package/convex/schema.ts +802 -0
- package/convex/searchLogs.d.ts +4 -0
- package/convex/searchLogs.d.ts.map +1 -0
- package/convex/searchLogs.js +129 -0
- package/convex/searchLogs.js.map +1 -0
- package/convex/searchLogs.ts +146 -0
- package/convex/seedAPILayerAPIs.d.ts +7 -0
- package/convex/seedAPILayerAPIs.d.ts.map +1 -0
- package/convex/seedAPILayerAPIs.js +177 -0
- package/convex/seedAPILayerAPIs.js.map +1 -0
- package/convex/seedAPILayerAPIs.ts +191 -0
- package/convex/seedDirectCallConfigs.d.ts +2 -0
- package/convex/seedDirectCallConfigs.d.ts.map +1 -0
- package/convex/seedDirectCallConfigs.js +324 -0
- package/convex/seedDirectCallConfigs.js.map +1 -0
- package/convex/seedDirectCallConfigs.ts +336 -0
- package/convex/seedPratham.d.ts +6 -0
- package/convex/seedPratham.d.ts.map +1 -0
- package/convex/seedPratham.js +150 -0
- package/convex/seedPratham.js.map +1 -0
- package/convex/seedPratham.ts +161 -0
- package/convex/spendAlerts.d.ts +36 -0
- package/convex/spendAlerts.d.ts.map +1 -0
- package/convex/spendAlerts.js +380 -0
- package/convex/spendAlerts.js.map +1 -0
- package/convex/spendAlerts.ts +442 -0
- package/convex/stripeActions.d.ts +19 -0
- package/convex/stripeActions.d.ts.map +1 -0
- package/convex/stripeActions.js +411 -0
- package/convex/stripeActions.js.map +1 -0
- package/convex/stripeActions.ts +512 -0
- package/convex/teams.d.ts +21 -0
- package/convex/teams.d.ts.map +1 -0
- package/convex/teams.js +215 -0
- package/convex/teams.js.map +1 -0
- package/convex/teams.ts +243 -0
- package/convex/telemetry.d.ts +4 -0
- package/convex/telemetry.d.ts.map +1 -0
- package/convex/telemetry.js +74 -0
- package/convex/telemetry.js.map +1 -0
- package/convex/telemetry.ts +81 -0
- package/convex/tsconfig.json +25 -0
- package/convex/updateAPIStatus.d.ts +6 -0
- package/convex/updateAPIStatus.d.ts.map +1 -0
- package/convex/updateAPIStatus.js +40 -0
- package/convex/updateAPIStatus.js.map +1 -0
- package/convex/updateAPIStatus.ts +45 -0
- package/convex/usage.d.ts +27 -0
- package/convex/usage.d.ts.map +1 -0
- package/convex/usage.js +229 -0
- package/convex/usage.js.map +1 -0
- package/convex/usage.ts +260 -0
- package/convex/waitlist.d.ts +4 -0
- package/convex/waitlist.d.ts.map +1 -0
- package/convex/waitlist.js +49 -0
- package/convex/waitlist.js.map +1 -0
- package/convex/waitlist.ts +55 -0
- package/convex/webhooks.d.ts +12 -0
- package/convex/webhooks.d.ts.map +1 -0
- package/convex/webhooks.js +410 -0
- package/convex/webhooks.js.map +1 -0
- package/convex/webhooks.ts +494 -0
- package/convex/workspaces.d.ts +31 -0
- package/convex/workspaces.d.ts.map +1 -0
- package/convex/workspaces.js +975 -0
- package/convex/workspaces.js.map +1 -0
- package/convex/workspaces.ts +1130 -0
- package/dist/bin.js +0 -0
- package/dist/capability-router.js +1 -1
- package/dist/capability-router.js.map +1 -1
- package/dist/execute.d.ts +2 -0
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +18 -4
- package/dist/execute.js.map +1 -1
- package/dist/http-api.js +1 -1
- package/dist/http-api.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-analytics.d.ts +32 -0
- package/dist/mcp-analytics.d.ts.map +1 -0
- package/dist/mcp-analytics.js +130 -0
- package/dist/mcp-analytics.js.map +1 -0
- package/package.json +1 -1
- package/dist/chain-types.d.ts +0 -187
- package/dist/chain-types.d.ts.map +0 -1
- package/dist/chain-types.js +0 -33
- package/dist/chain-types.js.map +0 -1
- package/dist/registry/apis.json.bak +0 -248811
- package/dist/src/bin.js +0 -17
- package/dist/src/capability-router.js +0 -240
- package/dist/src/chainExecutor.js +0 -451
- package/dist/src/chainResolver.js +0 -518
- package/dist/src/cli/commands/doctor.js +0 -324
- package/dist/src/cli/commands/mcp-install.js +0 -255
- package/dist/src/cli/commands/restore.js +0 -259
- package/dist/src/cli/commands/setup.js +0 -205
- package/dist/src/cli/commands/uninstall.js +0 -188
- package/dist/src/cli/index.js +0 -111
- package/dist/src/cli.js +0 -302
- package/dist/src/confirmation.js +0 -240
- package/dist/src/credentials.js +0 -357
- package/dist/src/credits.js +0 -260
- package/dist/src/crypto.js +0 -66
- package/dist/src/discovery.js +0 -504
- package/dist/src/enterprise/env.js +0 -123
- package/dist/src/enterprise/script-generator.js +0 -460
- package/dist/src/execute-dynamic.js +0 -473
- package/dist/src/execute.js +0 -1727
- package/dist/src/index.js +0 -2062
- package/dist/src/metered.js +0 -80
- package/dist/src/open-apis.js +0 -276
- package/dist/src/proxy.js +0 -28
- package/dist/src/session.js +0 -86
- package/dist/src/stripe.js +0 -407
- package/dist/src/telemetry.js +0 -49
- package/dist/src/types.js +0 -2
- package/dist/src/utils/backup.js +0 -181
- package/dist/src/utils/config.js +0 -220
- package/dist/src/utils/os.js +0 -105
- package/dist/src/utils/paths.js +0 -159
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* APIClaw Chain Executor
|
|
3
|
-
*
|
|
4
|
-
* Orchestrates multi-step API chains with:
|
|
5
|
-
* - Sequential execution
|
|
6
|
-
* - Parallel batches
|
|
7
|
-
* - Conditional branching
|
|
8
|
-
* - ForEach loops
|
|
9
|
-
* - Error handling and retry
|
|
10
|
-
* - Full execution trace
|
|
11
|
-
*/
|
|
12
|
-
import { executeAPICall } from './execute.js';
|
|
13
|
-
import { resolveReferences, validateReferences, evaluateCondition, } from './chainResolver.js';
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// MAIN EXECUTOR
|
|
16
|
-
// ============================================================================
|
|
17
|
-
/**
|
|
18
|
-
* Execute a chain of API calls
|
|
19
|
-
*/
|
|
20
|
-
export async function executeChain(chain, credentials, inputs, options = {}) {
|
|
21
|
-
const chainId = chain.id || generateChainId();
|
|
22
|
-
const startedAt = new Date().toISOString();
|
|
23
|
-
const startTime = Date.now();
|
|
24
|
-
// Initialize context
|
|
25
|
-
const context = {
|
|
26
|
-
results: {},
|
|
27
|
-
inputs: inputs || {},
|
|
28
|
-
currentIndex: 0,
|
|
29
|
-
startedAt,
|
|
30
|
-
env: process.env,
|
|
31
|
-
};
|
|
32
|
-
// Initialize result
|
|
33
|
-
const result = {
|
|
34
|
-
success: false,
|
|
35
|
-
chainId,
|
|
36
|
-
startedAt,
|
|
37
|
-
completedAt: '',
|
|
38
|
-
totalLatencyMs: 0,
|
|
39
|
-
totalCost: { cents: 0 },
|
|
40
|
-
results: {},
|
|
41
|
-
completedSteps: [],
|
|
42
|
-
trace: [],
|
|
43
|
-
canResume: false,
|
|
44
|
-
};
|
|
45
|
-
try {
|
|
46
|
-
// Validate inputs
|
|
47
|
-
if (chain.inputs) {
|
|
48
|
-
validateInputs(inputs || {}, chain.inputs);
|
|
49
|
-
}
|
|
50
|
-
// Validate references before execution
|
|
51
|
-
const validation = validateReferences(chain.steps);
|
|
52
|
-
if (!validation.valid) {
|
|
53
|
-
const errorMsg = validation.errors.map(e => `${e.stepId}: ${e.message}`).join('; ');
|
|
54
|
-
throw new Error(`Reference validation failed: ${errorMsg}`);
|
|
55
|
-
}
|
|
56
|
-
// Log warnings
|
|
57
|
-
if (options.verbose && validation.warnings.length > 0) {
|
|
58
|
-
console.log('[Chain] Warnings:', validation.warnings);
|
|
59
|
-
}
|
|
60
|
-
// Check limits
|
|
61
|
-
if (chain.limits?.maxSteps && chain.steps.length > chain.limits.maxSteps) {
|
|
62
|
-
throw new Error(`Chain exceeds maxSteps limit (${chain.steps.length} > ${chain.limits.maxSteps})`);
|
|
63
|
-
}
|
|
64
|
-
// Execute steps
|
|
65
|
-
let lastStepResult;
|
|
66
|
-
for (let i = 0; i < chain.steps.length; i++) {
|
|
67
|
-
const step = chain.steps[i];
|
|
68
|
-
context.currentIndex = i;
|
|
69
|
-
// Check timeout
|
|
70
|
-
if (chain.timeout && Date.now() - startTime > chain.timeout) {
|
|
71
|
-
throw new TimeoutError(`Chain exceeded timeout of ${chain.timeout}ms`);
|
|
72
|
-
}
|
|
73
|
-
// Execute based on step type
|
|
74
|
-
if (isParallelStep(step)) {
|
|
75
|
-
lastStepResult = await executeParallelStep(step, context, credentials, options, result);
|
|
76
|
-
}
|
|
77
|
-
else if (isConditionalStep(step)) {
|
|
78
|
-
lastStepResult = await executeConditionalStep(step, context, credentials, options, result);
|
|
79
|
-
}
|
|
80
|
-
else if (isForEachStep(step)) {
|
|
81
|
-
lastStepResult = await executeForEachStep(step, context, credentials, options, result, chain.limits);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
lastStepResult = await executeSingleStep(step, context, credentials, options, result, chain.errorPolicy);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Success!
|
|
88
|
-
result.success = true;
|
|
89
|
-
result.finalResult = lastStepResult;
|
|
90
|
-
result.results = { ...context.results };
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
// Handle failure
|
|
94
|
-
result.success = false;
|
|
95
|
-
result.error = {
|
|
96
|
-
stepId: result.failedStep?.stepId || 'unknown',
|
|
97
|
-
code: error.code || 'CHAIN_ERROR',
|
|
98
|
-
message: error.message,
|
|
99
|
-
retryAfter: error.retryAfter,
|
|
100
|
-
};
|
|
101
|
-
result.results = { ...context.results };
|
|
102
|
-
// Handle rollback for transactional mode
|
|
103
|
-
if (chain.errorPolicy?.mode === 'transactional' && chain.errorPolicy.rollback) {
|
|
104
|
-
await executeRollback(chain.errorPolicy.rollback, context, credentials, options, result);
|
|
105
|
-
}
|
|
106
|
-
// Can resume if we have completed steps
|
|
107
|
-
result.canResume = result.completedSteps.length > 0;
|
|
108
|
-
if (result.canResume) {
|
|
109
|
-
result.resumeToken = `${chainId}_step_${result.completedSteps.length}`;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
// Finalize
|
|
113
|
-
result.completedAt = new Date().toISOString();
|
|
114
|
-
result.totalLatencyMs = Date.now() - startTime;
|
|
115
|
-
return result;
|
|
116
|
-
}
|
|
117
|
-
// ============================================================================
|
|
118
|
-
// STEP TYPE GUARDS
|
|
119
|
-
// ============================================================================
|
|
120
|
-
function isParallelStep(step) {
|
|
121
|
-
return 'parallel' in step;
|
|
122
|
-
}
|
|
123
|
-
function isConditionalStep(step) {
|
|
124
|
-
return 'if' in step;
|
|
125
|
-
}
|
|
126
|
-
function isForEachStep(step) {
|
|
127
|
-
return 'forEach' in step;
|
|
128
|
-
}
|
|
129
|
-
// ============================================================================
|
|
130
|
-
// SINGLE STEP EXECUTION
|
|
131
|
-
// ============================================================================
|
|
132
|
-
async function executeSingleStep(step, context, credentials, options, result, errorPolicy) {
|
|
133
|
-
const stepStart = Date.now();
|
|
134
|
-
const stepTrace = {
|
|
135
|
-
stepId: step.id,
|
|
136
|
-
stepIndex: context.currentIndex,
|
|
137
|
-
provider: step.provider,
|
|
138
|
-
action: step.action,
|
|
139
|
-
startedAt: new Date().toISOString(),
|
|
140
|
-
completedAt: '',
|
|
141
|
-
latencyMs: 0,
|
|
142
|
-
success: false,
|
|
143
|
-
input: {},
|
|
144
|
-
};
|
|
145
|
-
options.onStepStart?.(step.id, context.currentIndex);
|
|
146
|
-
try {
|
|
147
|
-
// Resolve references in params
|
|
148
|
-
const resolvedParams = step.params ? resolveReferences(step.params, context) : {};
|
|
149
|
-
stepTrace.input = resolvedParams;
|
|
150
|
-
// Execute with retry if configured
|
|
151
|
-
const maxRetries = step.onError?.retry?.attempts || 0;
|
|
152
|
-
let lastError;
|
|
153
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
154
|
-
try {
|
|
155
|
-
// Get customer key for this provider
|
|
156
|
-
const customerKey = credentials.customerKeys?.[step.provider];
|
|
157
|
-
// Execute the API call
|
|
158
|
-
const apiResult = await executeAPICall(step.provider, step.action, resolvedParams, credentials.userId, customerKey);
|
|
159
|
-
if (!apiResult.success) {
|
|
160
|
-
throw new StepError(apiResult.error || 'API call failed', apiResult.code || 'PROVIDER_ERROR');
|
|
161
|
-
}
|
|
162
|
-
// Success!
|
|
163
|
-
stepTrace.success = true;
|
|
164
|
-
stepTrace.output = apiResult.data;
|
|
165
|
-
stepTrace.cost = apiResult.cost ? { cents: Math.round(apiResult.cost * 100) } : undefined;
|
|
166
|
-
// Update context
|
|
167
|
-
context.results[step.id] = apiResult.data;
|
|
168
|
-
context.prevStepId = step.id;
|
|
169
|
-
// Update result
|
|
170
|
-
result.completedSteps.push(step.id);
|
|
171
|
-
if (stepTrace.cost) {
|
|
172
|
-
result.totalCost.cents += stepTrace.cost.cents;
|
|
173
|
-
}
|
|
174
|
-
return apiResult.data;
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
lastError = error;
|
|
178
|
-
stepTrace.retries = attempt;
|
|
179
|
-
if (attempt < maxRetries) {
|
|
180
|
-
// Wait before retry
|
|
181
|
-
const backoff = step.onError?.retry?.backoff?.[attempt] || 1000 * Math.pow(2, attempt);
|
|
182
|
-
await sleep(backoff);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
// All retries exhausted
|
|
187
|
-
throw lastError;
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
stepTrace.success = false;
|
|
191
|
-
stepTrace.error = error.message;
|
|
192
|
-
stepTrace.errorCode = error.code;
|
|
193
|
-
// Handle error based on policy
|
|
194
|
-
const shouldAbort = step.onError?.abort !== false && errorPolicy?.mode !== 'best-effort';
|
|
195
|
-
// Try fallback if configured
|
|
196
|
-
if (step.onError?.fallback && !options.dryRun) {
|
|
197
|
-
try {
|
|
198
|
-
const fallbackResult = await executeSingleStep(step.onError.fallback, context, credentials, options, result, errorPolicy);
|
|
199
|
-
// Use fallback result as this step's result
|
|
200
|
-
context.results[step.id] = fallbackResult;
|
|
201
|
-
context.prevStepId = step.id;
|
|
202
|
-
stepTrace.success = true;
|
|
203
|
-
stepTrace.output = { fallback: true, result: fallbackResult };
|
|
204
|
-
result.completedSteps.push(step.id);
|
|
205
|
-
return fallbackResult;
|
|
206
|
-
}
|
|
207
|
-
catch (fallbackError) {
|
|
208
|
-
// Fallback also failed
|
|
209
|
-
stepTrace.error = `Original: ${error.message}; Fallback: ${fallbackError.message}`;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
result.failedStep = stepTrace;
|
|
213
|
-
if (shouldAbort) {
|
|
214
|
-
throw error;
|
|
215
|
-
}
|
|
216
|
-
// Best-effort mode: continue despite failure
|
|
217
|
-
context.results[step.id] = { error: error.message };
|
|
218
|
-
context.prevStepId = step.id;
|
|
219
|
-
return { error: error.message };
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
stepTrace.completedAt = new Date().toISOString();
|
|
223
|
-
stepTrace.latencyMs = Date.now() - stepStart;
|
|
224
|
-
result.trace.push(stepTrace);
|
|
225
|
-
options.onStepComplete?.(stepTrace);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// ============================================================================
|
|
229
|
-
// PARALLEL EXECUTION
|
|
230
|
-
// ============================================================================
|
|
231
|
-
async function executeParallelStep(step, context, credentials, options, result) {
|
|
232
|
-
const parallelResults = await Promise.all(step.parallel.map(async (ps, idx) => {
|
|
233
|
-
// Create a copy of context for each parallel step
|
|
234
|
-
const parallelContext = {
|
|
235
|
-
...context,
|
|
236
|
-
currentIndex: context.currentIndex,
|
|
237
|
-
};
|
|
238
|
-
try {
|
|
239
|
-
return await executeSingleStep(ps, parallelContext, credentials, options, result);
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
// Return error as result for best-effort
|
|
243
|
-
return { error: error.message };
|
|
244
|
-
}
|
|
245
|
-
}));
|
|
246
|
-
// Store parallel results
|
|
247
|
-
context.parallelResults = parallelResults;
|
|
248
|
-
// Copy results from parallel steps to main context
|
|
249
|
-
for (let i = 0; i < step.parallel.length; i++) {
|
|
250
|
-
const ps = step.parallel[i];
|
|
251
|
-
context.results[ps.id] = parallelResults[i];
|
|
252
|
-
}
|
|
253
|
-
// Last parallel step becomes prev
|
|
254
|
-
context.prevStepId = step.parallel[step.parallel.length - 1].id;
|
|
255
|
-
return parallelResults;
|
|
256
|
-
}
|
|
257
|
-
// ============================================================================
|
|
258
|
-
// CONDITIONAL EXECUTION
|
|
259
|
-
// ============================================================================
|
|
260
|
-
async function executeConditionalStep(step, context, credentials, options, result) {
|
|
261
|
-
// Evaluate condition
|
|
262
|
-
const conditionResult = evaluateCondition(step.if, context);
|
|
263
|
-
// Choose branch
|
|
264
|
-
const branch = conditionResult ? step.then : step.else;
|
|
265
|
-
if (!branch) {
|
|
266
|
-
// No else branch and condition is false
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
// Execute branch (can be single step or array)
|
|
270
|
-
const steps = Array.isArray(branch) ? branch : [branch];
|
|
271
|
-
let lastResult;
|
|
272
|
-
for (const branchStep of steps) {
|
|
273
|
-
lastResult = await executeSingleStep(branchStep, context, credentials, options, result);
|
|
274
|
-
}
|
|
275
|
-
return lastResult;
|
|
276
|
-
}
|
|
277
|
-
// ============================================================================
|
|
278
|
-
// FOREACH EXECUTION
|
|
279
|
-
// ============================================================================
|
|
280
|
-
async function executeForEachStep(step, context, credentials, options, result, limits) {
|
|
281
|
-
// Resolve the array to iterate over
|
|
282
|
-
const arrayRef = step.forEach;
|
|
283
|
-
let items;
|
|
284
|
-
// Handle reference vs direct array
|
|
285
|
-
if (arrayRef.startsWith('$')) {
|
|
286
|
-
// It's a reference - resolve it
|
|
287
|
-
const resolved = resolveReferences({ items: arrayRef }, context);
|
|
288
|
-
items = resolved.items;
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
// Try to parse as JSON array
|
|
292
|
-
try {
|
|
293
|
-
items = JSON.parse(arrayRef);
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
throw new Error(`forEach value must be a reference or JSON array: ${arrayRef}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if (!Array.isArray(items)) {
|
|
300
|
-
throw new Error(`forEach target must resolve to an array, got: ${typeof items}`);
|
|
301
|
-
}
|
|
302
|
-
// Check max iterations
|
|
303
|
-
if (limits?.maxSteps && items.length > limits.maxSteps) {
|
|
304
|
-
throw new Error(`forEach exceeds maxSteps limit (${items.length} iterations)`);
|
|
305
|
-
}
|
|
306
|
-
const forEachResults = [];
|
|
307
|
-
for (let i = 0; i < items.length; i++) {
|
|
308
|
-
// Set up forEach context
|
|
309
|
-
context.forEachItem = items[i];
|
|
310
|
-
context.forEachIndex = i;
|
|
311
|
-
context.forEachResults = forEachResults;
|
|
312
|
-
// Create step with dynamic ID
|
|
313
|
-
const dynamicStep = {
|
|
314
|
-
...step.do,
|
|
315
|
-
id: step.do.id.replace(/\$index/g, String(i)),
|
|
316
|
-
};
|
|
317
|
-
const itemResult = await executeSingleStep(dynamicStep, context, credentials, options, result);
|
|
318
|
-
forEachResults.push(itemResult);
|
|
319
|
-
}
|
|
320
|
-
// Clean up forEach context
|
|
321
|
-
delete context.forEachItem;
|
|
322
|
-
delete context.forEachIndex;
|
|
323
|
-
context.forEachResults = forEachResults;
|
|
324
|
-
// Store aggregate results
|
|
325
|
-
context.results['$forEach'] = { results: forEachResults };
|
|
326
|
-
return forEachResults;
|
|
327
|
-
}
|
|
328
|
-
// ============================================================================
|
|
329
|
-
// ROLLBACK EXECUTION
|
|
330
|
-
// ============================================================================
|
|
331
|
-
async function executeRollback(rollbackSteps, context, credentials, options, result) {
|
|
332
|
-
for (const rb of rollbackSteps) {
|
|
333
|
-
try {
|
|
334
|
-
const shouldRollback = evaluateCondition(rb.if, context);
|
|
335
|
-
if (shouldRollback) {
|
|
336
|
-
await executeSingleStep(rb.do, context, credentials, { ...options, verbose: false }, result);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
// Log but don't fail on rollback errors
|
|
341
|
-
console.error('[Chain] Rollback failed:', error);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
// ============================================================================
|
|
346
|
-
// INPUT VALIDATION
|
|
347
|
-
// ============================================================================
|
|
348
|
-
function validateInputs(inputs, definitions) {
|
|
349
|
-
for (const [name, def] of Object.entries(definitions)) {
|
|
350
|
-
const value = inputs[name];
|
|
351
|
-
// Check required
|
|
352
|
-
if (def.required && value === undefined) {
|
|
353
|
-
if (def.default !== undefined) {
|
|
354
|
-
inputs[name] = def.default;
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
throw new Error(`Missing required input: ${name}`);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
// Apply default
|
|
361
|
-
if (value === undefined && def.default !== undefined) {
|
|
362
|
-
inputs[name] = def.default;
|
|
363
|
-
}
|
|
364
|
-
// Type check (if value provided)
|
|
365
|
-
if (inputs[name] !== undefined) {
|
|
366
|
-
const actualType = Array.isArray(inputs[name]) ? 'array' : typeof inputs[name];
|
|
367
|
-
if (actualType !== def.type) {
|
|
368
|
-
throw new Error(`Input '${name}' must be ${def.type}, got ${actualType}`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// ============================================================================
|
|
374
|
-
// UTILITIES
|
|
375
|
-
// ============================================================================
|
|
376
|
-
function generateChainId() {
|
|
377
|
-
return `chain_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
378
|
-
}
|
|
379
|
-
function sleep(ms) {
|
|
380
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
381
|
-
}
|
|
382
|
-
// ============================================================================
|
|
383
|
-
// CUSTOM ERRORS
|
|
384
|
-
// ============================================================================
|
|
385
|
-
export class StepError extends Error {
|
|
386
|
-
constructor(message, code, retryAfter) {
|
|
387
|
-
super(message);
|
|
388
|
-
this.name = 'StepError';
|
|
389
|
-
this.code = code;
|
|
390
|
-
this.retryAfter = retryAfter;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
export class TimeoutError extends Error {
|
|
394
|
-
constructor(message) {
|
|
395
|
-
super(message);
|
|
396
|
-
this.code = 'TIMEOUT';
|
|
397
|
-
this.name = 'TimeoutError';
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
// ============================================================================
|
|
401
|
-
// CHAIN STATUS & RESUME (Stubs for future implementation)
|
|
402
|
-
// ============================================================================
|
|
403
|
-
// In-memory store for running chains (would be Redis/DB in production)
|
|
404
|
-
const runningChains = new Map();
|
|
405
|
-
/**
|
|
406
|
-
* Get status of a running or completed chain
|
|
407
|
-
*/
|
|
408
|
-
export async function getChainStatus(chainId) {
|
|
409
|
-
const entry = runningChains.get(chainId);
|
|
410
|
-
if (!entry) {
|
|
411
|
-
return { chainId, status: 'not_found' };
|
|
412
|
-
}
|
|
413
|
-
return {
|
|
414
|
-
chainId,
|
|
415
|
-
status: entry.status,
|
|
416
|
-
result: entry.result,
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* Resume a failed chain from a resume token
|
|
421
|
-
*/
|
|
422
|
-
export async function resumeChain(resumeToken, chain, credentials, inputs, overrides, options = {}) {
|
|
423
|
-
// Parse resume token: chain_xyz_step_N
|
|
424
|
-
const match = resumeToken.match(/^(.+)_step_(\d+)$/);
|
|
425
|
-
if (!match) {
|
|
426
|
-
throw new Error(`Invalid resume token: ${resumeToken}`);
|
|
427
|
-
}
|
|
428
|
-
const [, chainId, stepIndexStr] = match;
|
|
429
|
-
const resumeFromIndex = parseInt(stepIndexStr, 10);
|
|
430
|
-
// Create a modified chain starting from the resume point
|
|
431
|
-
const resumedChain = {
|
|
432
|
-
...chain,
|
|
433
|
-
id: chainId,
|
|
434
|
-
steps: chain.steps.slice(resumeFromIndex),
|
|
435
|
-
};
|
|
436
|
-
// Apply any overrides to the resumed steps
|
|
437
|
-
if (overrides) {
|
|
438
|
-
for (const step of resumedChain.steps) {
|
|
439
|
-
if ('id' in step && overrides[step.id]) {
|
|
440
|
-
step.params = { ...step.params, ...overrides[step.id] };
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
// Execute the resumed chain
|
|
445
|
-
// Note: In a real implementation, we'd restore the context from the previous run
|
|
446
|
-
return executeChain(resumedChain, credentials, inputs, options);
|
|
447
|
-
}
|
|
448
|
-
// ============================================================================
|
|
449
|
-
// RE-EXPORTS
|
|
450
|
-
// ============================================================================
|
|
451
|
-
export { resolveReferences, validateReferences, evaluateCondition, } from './chainResolver.js';
|