@ratio-mcp/dev-server 1.5.0 → 1.6.0
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/README.md +118 -0
- package/dist/schemas/app-distribution.json +36 -0
- package/dist/schemas/customers.json +26 -0
- package/dist/schemas/discounts.json +81 -0
- package/dist/schemas/draft_orders.json +112 -0
- package/dist/schemas/loyalty.json +147 -0
- package/dist/schemas/oauth.json +71 -0
- package/dist/schemas/orders.json +597 -102
- package/dist/schemas/product_reviews.json +45 -0
- package/dist/schemas/products.json +401 -118
- package/dist/schemas/tags.json +98 -0
- package/dist/schemas/variants.json +112 -0
- package/dist/schemas/webhooks.json +148 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +39 -5
- package/dist/server.js.map +1 -1
- package/dist/services/app-state-sync.d.ts +34 -0
- package/dist/services/app-state-sync.d.ts.map +1 -0
- package/dist/services/app-state-sync.js +85 -0
- package/dist/services/app-state-sync.js.map +1 -0
- package/dist/services/elicitation.d.ts +25 -0
- package/dist/services/elicitation.d.ts.map +1 -0
- package/dist/services/elicitation.js +101 -0
- package/dist/services/elicitation.js.map +1 -0
- package/dist/services/flow-profiles.d.ts +46 -0
- package/dist/services/flow-profiles.d.ts.map +1 -0
- package/dist/services/flow-profiles.js +100 -0
- package/dist/services/flow-profiles.js.map +1 -0
- package/dist/services/questionnaire.d.ts +89 -0
- package/dist/services/questionnaire.d.ts.map +1 -0
- package/dist/services/questionnaire.js +167 -0
- package/dist/services/questionnaire.js.map +1 -0
- package/dist/services/session-state.d.ts +17 -0
- package/dist/services/session-state.d.ts.map +1 -1
- package/dist/services/session-state.js +26 -1
- package/dist/services/session-state.js.map +1 -1
- package/dist/services/tool-guard.d.ts +15 -18
- package/dist/services/tool-guard.d.ts.map +1 -1
- package/dist/services/tool-guard.js +150 -97
- package/dist/services/tool-guard.js.map +1 -1
- package/dist/tools/app-management.d.ts.map +1 -1
- package/dist/tools/app-management.js +181 -33
- package/dist/tools/app-management.js.map +1 -1
- package/dist/tools/codegen.d.ts.map +1 -1
- package/dist/tools/codegen.js +21 -0
- package/dist/tools/codegen.js.map +1 -1
- package/dist/tools/gather-requirements.d.ts.map +1 -1
- package/dist/tools/gather-requirements.js +5 -3
- package/dist/tools/gather-requirements.js.map +1 -1
- package/dist/tools/get-status.d.ts.map +1 -1
- package/dist/tools/get-status.js +45 -34
- package/dist/tools/get-status.js.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lifecycle.d.ts.map +1 -1
- package/dist/tools/lifecycle.js +6 -3
- package/dist/tools/lifecycle.js.map +1 -1
- package/dist/tools/reference-data.d.ts +16 -0
- package/dist/tools/reference-data.d.ts.map +1 -1
- package/dist/tools/reference-data.js +12 -8
- package/dist/tools/reference-data.js.map +1 -1
- package/dist/tools/requirements.d.ts.map +1 -1
- package/dist/tools/requirements.js +17 -9
- package/dist/tools/requirements.js.map +1 -1
- package/dist/tools/submission.d.ts.map +1 -1
- package/dist/tools/submission.js +50 -13
- package/dist/tools/submission.js.map +1 -1
- package/dist/tools/webhooks.d.ts.map +1 -1
- package/dist/tools/webhooks.js +3 -1
- package/dist/tools/webhooks.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -2
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +19 -10
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/schema-resources.d.ts +8 -0
- package/dist/utils/schema-resources.d.ts.map +1 -0
- package/dist/utils/schema-resources.js +25 -0
- package/dist/utils/schema-resources.js.map +1 -0
- package/package.json +3 -3
- package/dist/orchestrated-index.d.ts +0 -11
- package/dist/orchestrated-index.d.ts.map +0 -1
- package/dist/orchestrated-index.js +0 -33
- package/dist/orchestrated-index.js.map +0 -1
- package/dist/orchestrated-server.d.ts +0 -21
- package/dist/orchestrated-server.d.ts.map +0 -1
- package/dist/orchestrated-server.js +0 -101
- package/dist/orchestrated-server.js.map +0 -1
- package/dist/services/orchestrator.d.ts +0 -63
- package/dist/services/orchestrator.d.ts.map +0 -1
- package/dist/services/orchestrator.js +0 -845
- package/dist/services/orchestrator.js.map +0 -1
- package/dist/services/response-formatter.d.ts +0 -24
- package/dist/services/response-formatter.d.ts.map +0 -1
- package/dist/services/response-formatter.js +0 -311
- package/dist/services/response-formatter.js.map +0 -1
- package/dist/tools/agent-chat.d.ts +0 -9
- package/dist/tools/agent-chat.d.ts.map +0 -1
- package/dist/tools/agent-chat.js +0 -465
- package/dist/tools/agent-chat.js.map +0 -1
|
@@ -1,845 +0,0 @@
|
|
|
1
|
-
import { getSession, hasCompleted, recordToolSuccess } from './session-state.js';
|
|
2
|
-
import { logger } from '../utils/logger.js';
|
|
3
|
-
// Helper: log the orchestrator decision for every phase
|
|
4
|
-
function logOrchestratorDecision(phase, toolToCall, toolArgs, needsInput) {
|
|
5
|
-
logger.phase(phase, `tool=${toolToCall}, needsInput=${needsInput}`);
|
|
6
|
-
logger.info(`[orchestrator] Phase: ${phase}`);
|
|
7
|
-
logger.info(`[orchestrator] Tool to call: ${toolToCall}`);
|
|
8
|
-
logger.info(`[orchestrator] Tool args: ${JSON.stringify(toolArgs)}`);
|
|
9
|
-
logger.info(`[orchestrator] Needs input: ${needsInput}`);
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Phase metadata: step count, description, whether it can be skipped.
|
|
13
|
-
*/
|
|
14
|
-
const PHASE_METADATA = {
|
|
15
|
-
auth: {
|
|
16
|
-
description: 'Developer Authentication',
|
|
17
|
-
skippable: false,
|
|
18
|
-
stepCount: 1,
|
|
19
|
-
},
|
|
20
|
-
app_setup: {
|
|
21
|
-
description: 'Create Application',
|
|
22
|
-
skippable: false,
|
|
23
|
-
stepCount: 2,
|
|
24
|
-
},
|
|
25
|
-
requirements: {
|
|
26
|
-
description: 'Define Requirements & Scopes',
|
|
27
|
-
skippable: false,
|
|
28
|
-
stepCount: 2,
|
|
29
|
-
},
|
|
30
|
-
webhooks: {
|
|
31
|
-
description: 'Configure Webhooks (Optional)',
|
|
32
|
-
skippable: true,
|
|
33
|
-
stepCount: 2,
|
|
34
|
-
},
|
|
35
|
-
frontend_build: {
|
|
36
|
-
description: 'Build & Submit Frontend',
|
|
37
|
-
skippable: false,
|
|
38
|
-
stepCount: 6,
|
|
39
|
-
},
|
|
40
|
-
review_wait: {
|
|
41
|
-
description: 'Wait for Admin Review',
|
|
42
|
-
skippable: false,
|
|
43
|
-
stepCount: 1,
|
|
44
|
-
},
|
|
45
|
-
publish_oauth: {
|
|
46
|
-
description: 'Publish & Complete OAuth',
|
|
47
|
-
skippable: false,
|
|
48
|
-
stepCount: 3,
|
|
49
|
-
},
|
|
50
|
-
backend_codegen: {
|
|
51
|
-
description: 'Generate Backend Code',
|
|
52
|
-
skippable: false,
|
|
53
|
-
stepCount: 2,
|
|
54
|
-
},
|
|
55
|
-
complete: {
|
|
56
|
-
description: 'Application Complete',
|
|
57
|
-
skippable: false,
|
|
58
|
-
stepCount: 1,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Calculate the absolute step number (across all phases) for progress tracking.
|
|
63
|
-
*/
|
|
64
|
-
function calculateAbsoluteStep(phase) {
|
|
65
|
-
const phases = [
|
|
66
|
-
'auth', 'app_setup', 'requirements', 'webhooks',
|
|
67
|
-
'frontend_build', 'review_wait', 'publish_oauth', 'backend_codegen', 'complete'
|
|
68
|
-
];
|
|
69
|
-
let step = 0;
|
|
70
|
-
for (const p of phases) {
|
|
71
|
-
if (p === phase)
|
|
72
|
-
break;
|
|
73
|
-
step += PHASE_METADATA[p].stepCount;
|
|
74
|
-
}
|
|
75
|
-
return step + 1;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Determine which phase the session is currently in.
|
|
79
|
-
*/
|
|
80
|
-
function determineCurrentPhase() {
|
|
81
|
-
const session = getSession();
|
|
82
|
-
// Phase 1: Auth - check if developer is logged in
|
|
83
|
-
if (!hasCompleted('developer_login')) {
|
|
84
|
-
return 'auth';
|
|
85
|
-
}
|
|
86
|
-
// Phase 2: App Setup - check if app exists
|
|
87
|
-
if (!hasCompleted('create_app')) {
|
|
88
|
-
// If categories haven't been fetched, we need to do that first
|
|
89
|
-
return 'app_setup';
|
|
90
|
-
}
|
|
91
|
-
// Phase 3: Requirements - check if scopes are defined
|
|
92
|
-
if (!session.context.scopesConfirmed) {
|
|
93
|
-
return 'requirements';
|
|
94
|
-
}
|
|
95
|
-
// Phase 4: Webhooks - optional phase
|
|
96
|
-
// Skip if user explicitly said skip_webhooks or if we detect they want to skip
|
|
97
|
-
// Otherwise include webhooks if not yet configured
|
|
98
|
-
if (!session.context.webhooksConfigured) {
|
|
99
|
-
// Note: Can be skipped by user
|
|
100
|
-
return 'webhooks';
|
|
101
|
-
}
|
|
102
|
-
// Phase 5: Frontend Build - scaffold, build, zip, upload, submit
|
|
103
|
-
if (!hasCompleted('submit_for_review')) {
|
|
104
|
-
return 'frontend_build';
|
|
105
|
-
}
|
|
106
|
-
// Phase 6: Review Wait - wait for get_app_status to show approved
|
|
107
|
-
if (!session.context.appApproved) {
|
|
108
|
-
return 'review_wait';
|
|
109
|
-
}
|
|
110
|
-
// Phase 7: Publish & OAuth - publish app, get auth code, exchange for access token
|
|
111
|
-
if (!session.context.oauthCompleted) {
|
|
112
|
-
return 'publish_oauth';
|
|
113
|
-
}
|
|
114
|
-
// Phase 8: Backend Codegen - scaffold backend, generate routes
|
|
115
|
-
if (!hasCompleted('generate_api_routes')) {
|
|
116
|
-
return 'backend_codegen';
|
|
117
|
-
}
|
|
118
|
-
// Phase 9: Complete
|
|
119
|
-
return 'complete';
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Determine the exact next step within the auth phase.
|
|
123
|
-
* Supports two flows:
|
|
124
|
-
* - Login: email + password → developer_login
|
|
125
|
-
* - Signup: display_name + email + password → developer_signup → developer_verify_otp → developer_login
|
|
126
|
-
*
|
|
127
|
-
* The user chooses auth_mode ("login" or "signup") in their first input.
|
|
128
|
-
*/
|
|
129
|
-
function getAuthPhaseStep(userInput) {
|
|
130
|
-
const session = getSession();
|
|
131
|
-
const progress = {
|
|
132
|
-
current: calculateAbsoluteStep('auth'),
|
|
133
|
-
total: 15,
|
|
134
|
-
percentage: Math.round((calculateAbsoluteStep('auth') / 15) * 100),
|
|
135
|
-
};
|
|
136
|
-
// If auth_mode was provided in input, store it
|
|
137
|
-
if (userInput?.['auth_mode']) {
|
|
138
|
-
session.context.authMode = userInput['auth_mode'];
|
|
139
|
-
}
|
|
140
|
-
const authMode = session.context.authMode;
|
|
141
|
-
// Step 0: Ask user to choose login or signup if not yet decided
|
|
142
|
-
if (!authMode) {
|
|
143
|
-
return {
|
|
144
|
-
phase: 'auth',
|
|
145
|
-
step: 'Choose Authentication Method',
|
|
146
|
-
toolToCall: 'developer_login', // default tool (will be overridden based on choice)
|
|
147
|
-
toolArgs: {},
|
|
148
|
-
needsInput: true,
|
|
149
|
-
inputPrompt: 'Do you have an existing Ratio developer account? Provide auth_mode as "login" or "signup".',
|
|
150
|
-
inputFields: ['auth_mode'],
|
|
151
|
-
message: 'Welcome! Do you have a Ratio developer account, or do you need to create one?',
|
|
152
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
153
|
-
progress,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// ── Login flow ──────────────────────────────────────────────────────
|
|
157
|
-
if (authMode === 'login') {
|
|
158
|
-
return {
|
|
159
|
-
phase: 'auth',
|
|
160
|
-
step: 'Developer Login',
|
|
161
|
-
toolToCall: 'developer_login',
|
|
162
|
-
toolArgs: {},
|
|
163
|
-
needsInput: true,
|
|
164
|
-
inputPrompt: 'Please provide your Ratio developer credentials to authenticate.',
|
|
165
|
-
inputFields: ['email', 'password'],
|
|
166
|
-
message: 'Authenticating developer account with Ratio...',
|
|
167
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
168
|
-
progress,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
// ── Signup flow ─────────────────────────────────────────────────────
|
|
172
|
-
// Step 1: Signup (register new account)
|
|
173
|
-
if (!hasCompleted('developer_signup')) {
|
|
174
|
-
return {
|
|
175
|
-
phase: 'auth',
|
|
176
|
-
step: 'Developer Signup',
|
|
177
|
-
toolToCall: 'developer_signup',
|
|
178
|
-
toolArgs: {},
|
|
179
|
-
needsInput: true,
|
|
180
|
-
inputPrompt: 'Please provide your details to create a new Ratio developer account.',
|
|
181
|
-
inputFields: ['display_name', 'email', 'password'],
|
|
182
|
-
message: 'Creating a new Ratio developer account...',
|
|
183
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
184
|
-
progress,
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
// Step 2: Verify OTP
|
|
188
|
-
if (!hasCompleted('developer_verify_otp')) {
|
|
189
|
-
return {
|
|
190
|
-
phase: 'auth',
|
|
191
|
-
step: 'Verify Email OTP',
|
|
192
|
-
toolToCall: 'developer_verify_otp',
|
|
193
|
-
toolArgs: {
|
|
194
|
-
email: session.context.signupEmail || '',
|
|
195
|
-
},
|
|
196
|
-
needsInput: true,
|
|
197
|
-
inputPrompt: 'Enter the verification code sent to your email.',
|
|
198
|
-
inputFields: ['code'],
|
|
199
|
-
message: 'A verification code was sent to your email. Please enter it to complete registration.',
|
|
200
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
201
|
-
progress,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
// Step 3: Login after signup + verification
|
|
205
|
-
return {
|
|
206
|
-
phase: 'auth',
|
|
207
|
-
step: 'Developer Login',
|
|
208
|
-
toolToCall: 'developer_login',
|
|
209
|
-
toolArgs: {},
|
|
210
|
-
needsInput: true,
|
|
211
|
-
inputPrompt: 'Your account is verified! Please provide your email and password to log in.',
|
|
212
|
-
inputFields: ['email', 'password'],
|
|
213
|
-
message: 'Account verified! Logging in...',
|
|
214
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
215
|
-
progress,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Determine the exact next step within the app_setup phase.
|
|
220
|
-
*/
|
|
221
|
-
function getAppSetupPhaseStep(userInput) {
|
|
222
|
-
const session = getSession();
|
|
223
|
-
// Step 1: Get categories (if not already done)
|
|
224
|
-
if (!hasCompleted('get_categories')) {
|
|
225
|
-
return {
|
|
226
|
-
phase: 'app_setup',
|
|
227
|
-
step: 'Fetch App Categories',
|
|
228
|
-
toolToCall: 'get_categories',
|
|
229
|
-
toolArgs: {},
|
|
230
|
-
needsInput: false,
|
|
231
|
-
message: 'Retrieving available app categories from Ratio...',
|
|
232
|
-
phase_description: PHASE_METADATA.app_setup.description,
|
|
233
|
-
progress: {
|
|
234
|
-
current: calculateAbsoluteStep('app_setup'),
|
|
235
|
-
total: 15,
|
|
236
|
-
percentage: Math.round((calculateAbsoluteStep('app_setup') / 15) * 100),
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
// Step 2: Create the app (needs user input for name, description, categories)
|
|
241
|
-
return {
|
|
242
|
-
phase: 'app_setup',
|
|
243
|
-
step: 'Create Application',
|
|
244
|
-
toolToCall: 'create_app',
|
|
245
|
-
toolArgs: {},
|
|
246
|
-
needsInput: true,
|
|
247
|
-
inputPrompt: 'IMPORTANT: You MUST present the fetched categories (from the previous step\'s data) to the developer as a numbered list ' +
|
|
248
|
-
'and ask them to choose which categories fit their app. Do NOT pick categories on the developer\'s behalf.\n\n' +
|
|
249
|
-
'Then ask the developer for:\n' +
|
|
250
|
-
'1. App name\n' +
|
|
251
|
-
'2. Short description of the app\n' +
|
|
252
|
-
'3. Which categories they want (from the list you showed)\n' +
|
|
253
|
-
'4. OAuth callback URL (suggest http://localhost:3000/callback as default)\n\n' +
|
|
254
|
-
'Once the developer confirms, provide all fields in input_data with category_ids as an array of slug strings.',
|
|
255
|
-
inputFields: ['name', 'description', 'category_ids', 'redirect_uri'],
|
|
256
|
-
message: 'Categories fetched. Now the developer needs to provide app details and select categories.',
|
|
257
|
-
phase_description: PHASE_METADATA.app_setup.description,
|
|
258
|
-
progress: {
|
|
259
|
-
current: calculateAbsoluteStep('app_setup') + 1,
|
|
260
|
-
total: 15,
|
|
261
|
-
percentage: Math.round(((calculateAbsoluteStep('app_setup') + 1) / 15) * 100),
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Determine the exact next step within the requirements phase.
|
|
267
|
-
*/
|
|
268
|
-
function getRequirementsPhaseStep(userInput) {
|
|
269
|
-
const session = getSession();
|
|
270
|
-
// Step 1: Gather requirements (NL → scope recommendations)
|
|
271
|
-
// If we already have the developer's app description (captured from their first message
|
|
272
|
-
// or from the app creation step), auto-feed it — no need to ask again.
|
|
273
|
-
if (!hasCompleted('gather_requirements')) {
|
|
274
|
-
const hasDescription = !!session.context.appDescription;
|
|
275
|
-
return {
|
|
276
|
-
phase: 'requirements',
|
|
277
|
-
step: 'Gather Requirements',
|
|
278
|
-
toolToCall: 'gather_requirements',
|
|
279
|
-
toolArgs: {
|
|
280
|
-
app_id: session.context.appId || '',
|
|
281
|
-
// Auto-fill from stored description if available
|
|
282
|
-
...(hasDescription ? { app_description: session.context.appDescription } : {}),
|
|
283
|
-
},
|
|
284
|
-
needsInput: !hasDescription,
|
|
285
|
-
inputPrompt: hasDescription
|
|
286
|
-
? undefined
|
|
287
|
-
: 'Describe what your app needs to do (e.g., "I want to view orders and manage discounts").',
|
|
288
|
-
inputFields: hasDescription ? undefined : ['app_description'],
|
|
289
|
-
message: hasDescription
|
|
290
|
-
? `Analyzing your requirements ("${session.context.appDescription.substring(0, 60)}...") to recommend scopes...`
|
|
291
|
-
: 'Please describe what your app needs to do so we can recommend the right scopes.',
|
|
292
|
-
phase_description: PHASE_METADATA.requirements.description,
|
|
293
|
-
progress: {
|
|
294
|
-
current: calculateAbsoluteStep('requirements'),
|
|
295
|
-
total: 15,
|
|
296
|
-
percentage: Math.round((calculateAbsoluteStep('requirements') / 15) * 100),
|
|
297
|
-
},
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
// Step 2: Define app requirements (confirm scopes from recommendations)
|
|
301
|
-
if (!session.context.scopesConfirmed) {
|
|
302
|
-
return {
|
|
303
|
-
phase: 'requirements',
|
|
304
|
-
step: 'Confirm Scopes',
|
|
305
|
-
toolToCall: 'define_app_requirements',
|
|
306
|
-
toolArgs: {
|
|
307
|
-
app_id: session.context.appId,
|
|
308
|
-
app_name: session.context.appName,
|
|
309
|
-
},
|
|
310
|
-
needsInput: true,
|
|
311
|
-
inputPrompt: 'IMPORTANT: You MUST present the recommended scopes (from the previous step\'s data) to the developer. ' +
|
|
312
|
-
'Show each scope code, its resource, and why it was recommended. ' +
|
|
313
|
-
'Do NOT confirm scopes on the developer\'s behalf — ask them to review and approve or modify the list.\n\n' +
|
|
314
|
-
'Once the developer confirms, call with input_data: { "scopes": ["read_orders", ...], "confirmed": true }',
|
|
315
|
-
inputFields: ['scopes', 'confirmed'],
|
|
316
|
-
message: 'Scope recommendations ready. The developer needs to review and confirm.',
|
|
317
|
-
phase_description: PHASE_METADATA.requirements.description,
|
|
318
|
-
progress: {
|
|
319
|
-
current: calculateAbsoluteStep('requirements') + 1,
|
|
320
|
-
total: 15,
|
|
321
|
-
percentage: Math.round(((calculateAbsoluteStep('requirements') + 1) / 15) * 100),
|
|
322
|
-
},
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
// Should not reach here if requirements are confirmed — fall through
|
|
326
|
-
return {
|
|
327
|
-
phase: 'requirements',
|
|
328
|
-
step: 'Requirements Complete',
|
|
329
|
-
toolToCall: 'gather_requirements',
|
|
330
|
-
toolArgs: { app_id: session.context.appId || '' },
|
|
331
|
-
needsInput: false,
|
|
332
|
-
message: 'Requirements have been confirmed. Moving to next phase...',
|
|
333
|
-
phase_description: PHASE_METADATA.requirements.description,
|
|
334
|
-
progress: {
|
|
335
|
-
current: calculateAbsoluteStep('requirements') + 2,
|
|
336
|
-
total: 15,
|
|
337
|
-
percentage: Math.round(((calculateAbsoluteStep('requirements') + 2) / 15) * 100),
|
|
338
|
-
},
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Determine the exact next step within the webhooks phase.
|
|
343
|
-
*/
|
|
344
|
-
function getWebhooksPhaseStep(userInput) {
|
|
345
|
-
const session = getSession();
|
|
346
|
-
// If user explicitly skipped webhooks, mark as configured
|
|
347
|
-
if (userInput?.skip === true) {
|
|
348
|
-
session.context.webhooksConfigured = true;
|
|
349
|
-
recordToolSuccess('skip_webhooks', { skipped: true });
|
|
350
|
-
return {
|
|
351
|
-
phase: 'webhooks',
|
|
352
|
-
step: 'Webhooks Skipped',
|
|
353
|
-
toolToCall: 'skip_webhooks',
|
|
354
|
-
toolArgs: {},
|
|
355
|
-
needsInput: false,
|
|
356
|
-
message: 'Webhook configuration skipped. You can set them up later in the dashboard.',
|
|
357
|
-
phase_description: PHASE_METADATA.webhooks.description,
|
|
358
|
-
progress: {
|
|
359
|
-
current: calculateAbsoluteStep('webhooks') + 2,
|
|
360
|
-
total: 15,
|
|
361
|
-
percentage: Math.round(((calculateAbsoluteStep('webhooks') + 2) / 15) * 100),
|
|
362
|
-
},
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
// Step 1: Get webhook topics (optional, but shows available events)
|
|
366
|
-
if (!hasCompleted('get_webhook_topics')) {
|
|
367
|
-
return {
|
|
368
|
-
phase: 'webhooks',
|
|
369
|
-
step: 'Fetch Webhook Topics',
|
|
370
|
-
toolToCall: 'get_webhook_topics',
|
|
371
|
-
toolArgs: {},
|
|
372
|
-
needsInput: false,
|
|
373
|
-
message: 'Retrieving available webhook event types...',
|
|
374
|
-
phase_description: PHASE_METADATA.webhooks.description,
|
|
375
|
-
progress: {
|
|
376
|
-
current: calculateAbsoluteStep('webhooks'),
|
|
377
|
-
total: 15,
|
|
378
|
-
percentage: Math.round((calculateAbsoluteStep('webhooks') / 15) * 100),
|
|
379
|
-
},
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
// Step 2: Create webhooks (or skip if user chooses)
|
|
383
|
-
return {
|
|
384
|
-
phase: 'webhooks',
|
|
385
|
-
step: 'Create Webhook Subscriptions',
|
|
386
|
-
toolToCall: 'create_webhooks',
|
|
387
|
-
toolArgs: {
|
|
388
|
-
app_id: session.context.appId,
|
|
389
|
-
},
|
|
390
|
-
needsInput: true,
|
|
391
|
-
inputPrompt: 'Provide your webhook endpoint URL and select which events to subscribe to. Or enter "skip" to skip webhook setup.',
|
|
392
|
-
inputFields: ['webhook_url', 'event_names'],
|
|
393
|
-
message: 'Setting up webhook subscriptions for real-time events...',
|
|
394
|
-
phase_description: PHASE_METADATA.webhooks.description,
|
|
395
|
-
progress: {
|
|
396
|
-
current: calculateAbsoluteStep('webhooks') + 1,
|
|
397
|
-
total: 15,
|
|
398
|
-
percentage: Math.round(((calculateAbsoluteStep('webhooks') + 1) / 15) * 100),
|
|
399
|
-
},
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Determine the exact next step within the frontend_build phase.
|
|
404
|
-
*/
|
|
405
|
-
function getFrontendBuildPhaseStep(userInput) {
|
|
406
|
-
const session = getSession();
|
|
407
|
-
// Step 1: Scaffold frontend
|
|
408
|
-
if (!hasCompleted('scaffold_frontend')) {
|
|
409
|
-
return {
|
|
410
|
-
phase: 'frontend_build',
|
|
411
|
-
step: 'Scaffold Frontend Project',
|
|
412
|
-
toolToCall: 'scaffold_frontend',
|
|
413
|
-
toolArgs: {
|
|
414
|
-
app_name: session.context.appName,
|
|
415
|
-
client_id: session.context.clientId,
|
|
416
|
-
project_path: '~/Desktop',
|
|
417
|
-
},
|
|
418
|
-
needsInput: true,
|
|
419
|
-
inputPrompt: 'Ask the developer what they want to name the project (project_name). The project will be created on their Desktop by default.',
|
|
420
|
-
inputFields: ['project_name'],
|
|
421
|
-
message: 'Creating a new React + Vite frontend project...',
|
|
422
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
423
|
-
progress: {
|
|
424
|
-
current: calculateAbsoluteStep('frontend_build'),
|
|
425
|
-
total: 15,
|
|
426
|
-
percentage: Math.round((calculateAbsoluteStep('frontend_build') / 15) * 100),
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
// Step 2: Build frontend
|
|
431
|
-
if (!hasCompleted('build_frontend')) {
|
|
432
|
-
return {
|
|
433
|
-
phase: 'frontend_build',
|
|
434
|
-
step: 'Build Frontend',
|
|
435
|
-
toolToCall: 'build_frontend',
|
|
436
|
-
toolArgs: {
|
|
437
|
-
project_path: session.context.projectPath || '',
|
|
438
|
-
},
|
|
439
|
-
needsInput: false,
|
|
440
|
-
message: 'Building production frontend bundle...',
|
|
441
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
442
|
-
progress: {
|
|
443
|
-
current: calculateAbsoluteStep('frontend_build') + 1,
|
|
444
|
-
total: 15,
|
|
445
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 1) / 15) * 100),
|
|
446
|
-
},
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
// Step 3: Validate build
|
|
450
|
-
if (!hasCompleted('validate_build')) {
|
|
451
|
-
return {
|
|
452
|
-
phase: 'frontend_build',
|
|
453
|
-
step: 'Validate Build',
|
|
454
|
-
toolToCall: 'validate_build',
|
|
455
|
-
toolArgs: {
|
|
456
|
-
build_path: session.context.buildPath || '',
|
|
457
|
-
},
|
|
458
|
-
needsInput: false,
|
|
459
|
-
message: 'Validating build meets Ratio submission requirements...',
|
|
460
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
461
|
-
progress: {
|
|
462
|
-
current: calculateAbsoluteStep('frontend_build') + 2,
|
|
463
|
-
total: 15,
|
|
464
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 2) / 15) * 100),
|
|
465
|
-
},
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
// Step 4: Create submission ZIP
|
|
469
|
-
if (!hasCompleted('create_submission_zip')) {
|
|
470
|
-
return {
|
|
471
|
-
phase: 'frontend_build',
|
|
472
|
-
step: 'Create Submission Archive',
|
|
473
|
-
toolToCall: 'create_submission_zip',
|
|
474
|
-
toolArgs: {
|
|
475
|
-
build_path: session.context.buildPath || '',
|
|
476
|
-
},
|
|
477
|
-
needsInput: false,
|
|
478
|
-
message: 'Creating ZIP archive for submission...',
|
|
479
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
480
|
-
progress: {
|
|
481
|
-
current: calculateAbsoluteStep('frontend_build') + 3,
|
|
482
|
-
total: 15,
|
|
483
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 3) / 15) * 100),
|
|
484
|
-
},
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
// Step 5: Upload build ZIP to create a version
|
|
488
|
-
if (!hasCompleted('upload_build')) {
|
|
489
|
-
return {
|
|
490
|
-
phase: 'frontend_build',
|
|
491
|
-
step: 'Upload Build',
|
|
492
|
-
toolToCall: 'upload_build',
|
|
493
|
-
toolArgs: {
|
|
494
|
-
app_id: session.context.appId,
|
|
495
|
-
zip_path: session.context.zipPath || '',
|
|
496
|
-
},
|
|
497
|
-
needsInput: true,
|
|
498
|
-
inputPrompt: 'Provide version number and changelog for your build upload.',
|
|
499
|
-
inputFields: ['version', 'changelog'],
|
|
500
|
-
message: 'Uploading build ZIP to create a new app version...',
|
|
501
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
502
|
-
progress: {
|
|
503
|
-
current: calculateAbsoluteStep('frontend_build') + 4,
|
|
504
|
-
total: 15,
|
|
505
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 4) / 15) * 100),
|
|
506
|
-
},
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
// Step 6: Submit for review
|
|
510
|
-
if (!hasCompleted('submit_for_review')) {
|
|
511
|
-
return {
|
|
512
|
-
phase: 'frontend_build',
|
|
513
|
-
step: 'Submit for Review',
|
|
514
|
-
toolToCall: 'submit_for_review',
|
|
515
|
-
toolArgs: {
|
|
516
|
-
app_id: session.context.appId,
|
|
517
|
-
},
|
|
518
|
-
needsInput: false,
|
|
519
|
-
message: 'Submitting app for admin review...',
|
|
520
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
521
|
-
progress: {
|
|
522
|
-
current: calculateAbsoluteStep('frontend_build') + 5,
|
|
523
|
-
total: 15,
|
|
524
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 5) / 15) * 100),
|
|
525
|
-
},
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
// Should not reach here
|
|
529
|
-
return {
|
|
530
|
-
phase: 'frontend_build',
|
|
531
|
-
step: 'Frontend Build Complete',
|
|
532
|
-
toolToCall: 'get_app_status',
|
|
533
|
-
toolArgs: { app_id: session.context.appId },
|
|
534
|
-
needsInput: false,
|
|
535
|
-
message: 'Frontend build and submission complete.',
|
|
536
|
-
phase_description: PHASE_METADATA.frontend_build.description,
|
|
537
|
-
progress: {
|
|
538
|
-
current: calculateAbsoluteStep('frontend_build') + 5,
|
|
539
|
-
total: 15,
|
|
540
|
-
percentage: Math.round(((calculateAbsoluteStep('frontend_build') + 5) / 15) * 100),
|
|
541
|
-
},
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Determine the exact next step within the review_wait phase.
|
|
546
|
-
*/
|
|
547
|
-
function getReviewWaitPhaseStep(userInput) {
|
|
548
|
-
const session = getSession();
|
|
549
|
-
return {
|
|
550
|
-
phase: 'review_wait',
|
|
551
|
-
step: 'Wait for Admin Review',
|
|
552
|
-
toolToCall: 'get_app_status',
|
|
553
|
-
toolArgs: {
|
|
554
|
-
app_id: session.context.appId,
|
|
555
|
-
},
|
|
556
|
-
needsInput: false,
|
|
557
|
-
message: 'Checking admin review status... Your app will be reviewed shortly.',
|
|
558
|
-
phase_description: PHASE_METADATA.review_wait.description,
|
|
559
|
-
progress: {
|
|
560
|
-
current: calculateAbsoluteStep('review_wait'),
|
|
561
|
-
total: 15,
|
|
562
|
-
percentage: Math.round((calculateAbsoluteStep('review_wait') / 15) * 100),
|
|
563
|
-
},
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Determine the exact next step within the publish_oauth phase.
|
|
568
|
-
*/
|
|
569
|
-
function getPublishOAuthPhaseStep(userInput) {
|
|
570
|
-
const session = getSession();
|
|
571
|
-
logger.info(`[orchestrator] publish_oauth phase — clientId=${session.context.clientId}, clientSecret exists=${!!session.context.clientSecret}, appId=${session.context.appId}, appPublished=${session.context.appPublished}, oauthCompleted=${session.context.oauthCompleted}`);
|
|
572
|
-
logger.info(`[orchestrator] publish_oauth — authCode=${session.context.authCode?.substring(0, 12) ?? 'undefined'}..., latestVersionId=${session.context.latestVersionId}`);
|
|
573
|
-
// Step 1: Publish app (if not already done)
|
|
574
|
-
if (!hasCompleted('publish_app')) {
|
|
575
|
-
return {
|
|
576
|
-
phase: 'publish_oauth',
|
|
577
|
-
step: 'Publish Application',
|
|
578
|
-
toolToCall: 'publish_app',
|
|
579
|
-
toolArgs: {
|
|
580
|
-
app_id: session.context.appId,
|
|
581
|
-
version_id: session.context.latestVersionId || '',
|
|
582
|
-
},
|
|
583
|
-
needsInput: false,
|
|
584
|
-
message: 'Publishing your approved app to the Ratio marketplace...',
|
|
585
|
-
phase_description: PHASE_METADATA.publish_oauth.description,
|
|
586
|
-
progress: {
|
|
587
|
-
current: calculateAbsoluteStep('publish_oauth'),
|
|
588
|
-
total: 15,
|
|
589
|
-
percentage: Math.round((calculateAbsoluteStep('publish_oauth') / 15) * 100),
|
|
590
|
-
},
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
// Step 2: Request authorization code
|
|
594
|
-
if (!hasCompleted('request_auth_code')) {
|
|
595
|
-
return {
|
|
596
|
-
phase: 'publish_oauth',
|
|
597
|
-
step: 'Request Authorization Code',
|
|
598
|
-
toolToCall: 'request_auth_code',
|
|
599
|
-
toolArgs: {
|
|
600
|
-
client_id: session.context.clientId,
|
|
601
|
-
app_id: session.context.appId,
|
|
602
|
-
scopes: session.context.confirmedScopes,
|
|
603
|
-
},
|
|
604
|
-
needsInput: true,
|
|
605
|
-
inputPrompt: 'Provide the merchant ID for OAuth testing.',
|
|
606
|
-
inputFields: ['merchant_id'],
|
|
607
|
-
message: 'Requesting OAuth authorization code for app installation...',
|
|
608
|
-
phase_description: PHASE_METADATA.publish_oauth.description,
|
|
609
|
-
progress: {
|
|
610
|
-
current: calculateAbsoluteStep('publish_oauth') + 1,
|
|
611
|
-
total: 15,
|
|
612
|
-
percentage: Math.round(((calculateAbsoluteStep('publish_oauth') + 1) / 15) * 100),
|
|
613
|
-
},
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
// Step 3: Exchange auth code for access token
|
|
617
|
-
if (!hasCompleted('get_access_token')) {
|
|
618
|
-
const hasAuthCode = !!session.context.authCode;
|
|
619
|
-
return {
|
|
620
|
-
phase: 'publish_oauth',
|
|
621
|
-
step: 'Exchange for Access Token',
|
|
622
|
-
toolToCall: 'get_access_token',
|
|
623
|
-
toolArgs: {
|
|
624
|
-
client_id: session.context.clientId,
|
|
625
|
-
client_secret: session.context.clientSecret,
|
|
626
|
-
...(hasAuthCode ? {
|
|
627
|
-
auth_code: session.context.authCode,
|
|
628
|
-
redirect_uri: session.context.authCodeRedirectUri || 'http://localhost:3000/callback',
|
|
629
|
-
} : {}),
|
|
630
|
-
},
|
|
631
|
-
needsInput: !hasAuthCode,
|
|
632
|
-
inputPrompt: hasAuthCode ? undefined : 'Provide the authorization code received from the OAuth flow.',
|
|
633
|
-
inputFields: hasAuthCode ? undefined : ['auth_code'],
|
|
634
|
-
message: hasAuthCode
|
|
635
|
-
? 'Auth code available. Exchanging for access and refresh tokens...'
|
|
636
|
-
: 'Exchanging authorization code for access and refresh tokens...',
|
|
637
|
-
phase_description: PHASE_METADATA.publish_oauth.description,
|
|
638
|
-
progress: {
|
|
639
|
-
current: calculateAbsoluteStep('publish_oauth') + 2,
|
|
640
|
-
total: 15,
|
|
641
|
-
percentage: Math.round(((calculateAbsoluteStep('publish_oauth') + 2) / 15) * 100),
|
|
642
|
-
},
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
return {
|
|
646
|
-
phase: 'publish_oauth',
|
|
647
|
-
step: 'OAuth Complete',
|
|
648
|
-
toolToCall: 'check_token_status',
|
|
649
|
-
toolArgs: { client_id: session.context.clientId },
|
|
650
|
-
needsInput: false,
|
|
651
|
-
message: 'OAuth flow complete. Access token is ready for use.',
|
|
652
|
-
phase_description: PHASE_METADATA.publish_oauth.description,
|
|
653
|
-
progress: {
|
|
654
|
-
current: calculateAbsoluteStep('publish_oauth') + 3,
|
|
655
|
-
total: 15,
|
|
656
|
-
percentage: Math.round(((calculateAbsoluteStep('publish_oauth') + 3) / 15) * 100),
|
|
657
|
-
},
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Determine the exact next step within the backend_codegen phase.
|
|
662
|
-
*/
|
|
663
|
-
function getBackendCodegenPhaseStep(userInput) {
|
|
664
|
-
const session = getSession();
|
|
665
|
-
logger.info(`[orchestrator] backend_codegen phase — clientId=${session.context.clientId}, hasClientSecret=${!!session.context.clientSecret}, projectPath=${session.context.projectPath}, appId=${session.context.appId}`);
|
|
666
|
-
logger.info(`[orchestrator] backend_codegen — oauthCompleted=${session.context.oauthCompleted}, completedTools=[${[...session.completedTools].join(', ')}]`);
|
|
667
|
-
// Step 1: Scaffold backend
|
|
668
|
-
if (!hasCompleted('scaffold_backend')) {
|
|
669
|
-
const hasProjectPath = !!session.context.projectPath;
|
|
670
|
-
logger.info(`[orchestrator] scaffold_backend about to be called with: app_name=${session.context.appName}, client_id=${session.context.clientId}, hasClientSecret=${!!session.context.clientSecret}, hasProjectPath=${hasProjectPath}, projectPath=${session.context.projectPath}`);
|
|
671
|
-
const toolArgs = {
|
|
672
|
-
app_name: session.context.appName,
|
|
673
|
-
client_id: session.context.clientId,
|
|
674
|
-
client_secret: session.context.clientSecret,
|
|
675
|
-
...(hasProjectPath ? { project_path: session.context.projectPath } : {}),
|
|
676
|
-
};
|
|
677
|
-
logOrchestratorDecision('backend_codegen', 'scaffold_backend', toolArgs, !hasProjectPath);
|
|
678
|
-
return {
|
|
679
|
-
phase: 'backend_codegen',
|
|
680
|
-
step: 'Scaffold Backend Project',
|
|
681
|
-
toolToCall: 'scaffold_backend',
|
|
682
|
-
toolArgs,
|
|
683
|
-
needsInput: !hasProjectPath,
|
|
684
|
-
inputPrompt: hasProjectPath ? undefined : 'Provide the path to your project directory.',
|
|
685
|
-
inputFields: hasProjectPath ? undefined : ['project_path'],
|
|
686
|
-
message: hasProjectPath
|
|
687
|
-
? `Creating NestJS backend at ${session.context.projectPath}/backend...`
|
|
688
|
-
: 'Creating a NestJS backend project with OAuth credentials...',
|
|
689
|
-
phase_description: PHASE_METADATA.backend_codegen.description,
|
|
690
|
-
progress: {
|
|
691
|
-
current: calculateAbsoluteStep('backend_codegen'),
|
|
692
|
-
total: 15,
|
|
693
|
-
percentage: Math.round((calculateAbsoluteStep('backend_codegen') / 15) * 100),
|
|
694
|
-
},
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
// Step 2: Generate API routes
|
|
698
|
-
if (!hasCompleted('generate_api_routes')) {
|
|
699
|
-
const genArgs = {
|
|
700
|
-
app_id: session.context.appId,
|
|
701
|
-
project_path: session.context.projectPath || '',
|
|
702
|
-
};
|
|
703
|
-
logger.info(`[orchestrator] generate_api_routes about to be called with: app_id=${session.context.appId}, project_path=${session.context.projectPath}`);
|
|
704
|
-
logOrchestratorDecision('backend_codegen', 'generate_api_routes', genArgs, false);
|
|
705
|
-
return {
|
|
706
|
-
phase: 'backend_codegen',
|
|
707
|
-
step: 'Generate API Routes',
|
|
708
|
-
toolToCall: 'generate_api_routes',
|
|
709
|
-
toolArgs: genArgs,
|
|
710
|
-
needsInput: false,
|
|
711
|
-
message: 'Generating NestJS controllers and services based on your scopes...',
|
|
712
|
-
phase_description: PHASE_METADATA.backend_codegen.description,
|
|
713
|
-
progress: {
|
|
714
|
-
current: calculateAbsoluteStep('backend_codegen') + 1,
|
|
715
|
-
total: 15,
|
|
716
|
-
percentage: Math.round(((calculateAbsoluteStep('backend_codegen') + 1) / 15) * 100),
|
|
717
|
-
},
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
return {
|
|
721
|
-
phase: 'backend_codegen',
|
|
722
|
-
step: 'Backend Generation Complete',
|
|
723
|
-
toolToCall: 'generate_api_routes',
|
|
724
|
-
toolArgs: { app_id: session.context.appId, project_path: session.context.projectPath || '' },
|
|
725
|
-
needsInput: false,
|
|
726
|
-
message: 'Backend code generation complete. Your API routes are ready.',
|
|
727
|
-
phase_description: PHASE_METADATA.backend_codegen.description,
|
|
728
|
-
progress: {
|
|
729
|
-
current: calculateAbsoluteStep('backend_codegen') + 2,
|
|
730
|
-
total: 15,
|
|
731
|
-
percentage: Math.round(((calculateAbsoluteStep('backend_codegen') + 2) / 15) * 100),
|
|
732
|
-
},
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Determine the exact next step in the complete phase.
|
|
737
|
-
*/
|
|
738
|
-
function getCompletePhaseStep() {
|
|
739
|
-
return {
|
|
740
|
-
phase: 'complete',
|
|
741
|
-
step: 'All Done!',
|
|
742
|
-
toolToCall: 'noop',
|
|
743
|
-
toolArgs: {},
|
|
744
|
-
needsInput: false,
|
|
745
|
-
message: 'Congratulations! Your Ratio app development is complete. You can now manage your app in the dashboard.',
|
|
746
|
-
phase_description: PHASE_METADATA.complete.description,
|
|
747
|
-
progress: {
|
|
748
|
-
current: 15,
|
|
749
|
-
total: 15,
|
|
750
|
-
percentage: 100,
|
|
751
|
-
},
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* Main orchestrator function: determines the exact next step based on session state.
|
|
756
|
-
*
|
|
757
|
-
* @param userInput - Optional user input (credentials, selections, configuration details)
|
|
758
|
-
* @returns OrchestratorResult describing what to do next
|
|
759
|
-
*/
|
|
760
|
-
export function getNextStep(userInput) {
|
|
761
|
-
try {
|
|
762
|
-
const phase = determineCurrentPhase();
|
|
763
|
-
switch (phase) {
|
|
764
|
-
case 'auth':
|
|
765
|
-
return getAuthPhaseStep(userInput);
|
|
766
|
-
case 'app_setup':
|
|
767
|
-
return getAppSetupPhaseStep(userInput);
|
|
768
|
-
case 'requirements':
|
|
769
|
-
return getRequirementsPhaseStep(userInput);
|
|
770
|
-
case 'webhooks':
|
|
771
|
-
return getWebhooksPhaseStep(userInput);
|
|
772
|
-
case 'frontend_build':
|
|
773
|
-
return getFrontendBuildPhaseStep(userInput);
|
|
774
|
-
case 'review_wait':
|
|
775
|
-
return getReviewWaitPhaseStep(userInput);
|
|
776
|
-
case 'publish_oauth':
|
|
777
|
-
return getPublishOAuthPhaseStep(userInput);
|
|
778
|
-
case 'backend_codegen':
|
|
779
|
-
return getBackendCodegenPhaseStep(userInput);
|
|
780
|
-
case 'complete':
|
|
781
|
-
return getCompletePhaseStep();
|
|
782
|
-
default:
|
|
783
|
-
// Fallback (should never reach)
|
|
784
|
-
return getCompletePhaseStep();
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
catch (error) {
|
|
788
|
-
// If anything goes wrong, return a safe default
|
|
789
|
-
logger.error('Orchestrator error:', error);
|
|
790
|
-
return {
|
|
791
|
-
phase: 'auth',
|
|
792
|
-
step: 'Error - Restart',
|
|
793
|
-
toolToCall: 'developer_login',
|
|
794
|
-
toolArgs: {},
|
|
795
|
-
needsInput: true,
|
|
796
|
-
inputPrompt: 'An error occurred. Please try authenticating again.',
|
|
797
|
-
inputFields: ['email', 'password'],
|
|
798
|
-
message: 'An error occurred in the orchestrator. Please restart.',
|
|
799
|
-
phase_description: PHASE_METADATA.auth.description,
|
|
800
|
-
progress: { current: 0, total: 15, percentage: 0 },
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Get the current phase without computing the next step details.
|
|
806
|
-
* Useful for status checks and phase-only queries.
|
|
807
|
-
*/
|
|
808
|
-
export function getCurrentPhase() {
|
|
809
|
-
return determineCurrentPhase();
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* Get a human-friendly description of a phase.
|
|
813
|
-
*/
|
|
814
|
-
export function getPhaseDescription(phase) {
|
|
815
|
-
return PHASE_METADATA[phase]?.description || 'Unknown Phase';
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Check if a phase can be skipped by the user.
|
|
819
|
-
* Currently only webhooks is optional.
|
|
820
|
-
*/
|
|
821
|
-
export function canSkipPhase(phase) {
|
|
822
|
-
return PHASE_METADATA[phase]?.skippable ?? false;
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Get the total number of steps across all phases.
|
|
826
|
-
*/
|
|
827
|
-
export function getTotalSteps() {
|
|
828
|
-
return Object.values(PHASE_METADATA).reduce((sum, meta) => sum + meta.stepCount, 0);
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Get metadata about a specific phase (description, step count, skippable).
|
|
832
|
-
*/
|
|
833
|
-
export function getPhaseMetadata(phase) {
|
|
834
|
-
return PHASE_METADATA[phase];
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Helper: Get all phases in order.
|
|
838
|
-
*/
|
|
839
|
-
export function getAllPhases() {
|
|
840
|
-
return [
|
|
841
|
-
'auth', 'app_setup', 'requirements', 'webhooks',
|
|
842
|
-
'frontend_build', 'review_wait', 'publish_oauth', 'backend_codegen', 'complete'
|
|
843
|
-
];
|
|
844
|
-
}
|
|
845
|
-
//# sourceMappingURL=orchestrator.js.map
|