@velt-js/mcp-installer 0.1.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 +373 -0
- package/bin/mcp-server.js +22 -0
- package/package.json +42 -0
- package/src/index.js +754 -0
- package/src/tools/orchestrator.js +299 -0
- package/src/tools/unified-installer.js +886 -0
- package/src/utils/cli.js +380 -0
- package/src/utils/comment-detector.js +305 -0
- package/src/utils/config.js +149 -0
- package/src/utils/framework-detection.js +262 -0
- package/src/utils/header-positioning.js +146 -0
- package/src/utils/host-app-discovery.js +1000 -0
- package/src/utils/integration.js +803 -0
- package/src/utils/plan-formatter.js +1698 -0
- package/src/utils/screenshot.js +151 -0
- package/src/utils/use-client.js +366 -0
- package/src/utils/validation.js +556 -0
- package/src/utils/velt-docs-fetcher.js +288 -0
- package/src/utils/velt-docs-urls.js +140 -0
- package/src/utils/velt-mcp-client.js +202 -0
- package/src/utils/velt-mcp.js +718 -0
|
@@ -0,0 +1,886 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Velt Installer
|
|
3
|
+
*
|
|
4
|
+
* Single orchestration module that handles both installation paths:
|
|
5
|
+
* 1. CLI-only (SKIP path): Run Velt CLI scaffolding only, basic QA
|
|
6
|
+
* 2. Guided path: Generate plan, await approval, apply edits, full QA
|
|
7
|
+
*
|
|
8
|
+
* Uses npx @velt-js/add-velt to run the published Velt CLI package.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { runVeltCliWithFeatures, runVeltCliCoreOnly } from '../utils/cli.js';
|
|
12
|
+
import { detectLibraries } from '../utils/velt-mcp.js';
|
|
13
|
+
import { getFrameworkInfo } from '../utils/framework-detection.js';
|
|
14
|
+
import {
|
|
15
|
+
fetchCommentImplementation,
|
|
16
|
+
fetchCrdtImplementation,
|
|
17
|
+
fetchFeatureImplementation,
|
|
18
|
+
} from '../utils/velt-docs-fetcher.js';
|
|
19
|
+
import {
|
|
20
|
+
createVeltCommentsPlan,
|
|
21
|
+
createMultiFeaturePlan,
|
|
22
|
+
createCliOnlyReport,
|
|
23
|
+
} from '../utils/plan-formatter.js';
|
|
24
|
+
import {
|
|
25
|
+
validateNextJsProject,
|
|
26
|
+
validateBasicCliInstall,
|
|
27
|
+
validateInstallation,
|
|
28
|
+
applyUseClientFixes as applyFixes,
|
|
29
|
+
} from '../utils/validation.js';
|
|
30
|
+
import {
|
|
31
|
+
discoverHostAppWiring,
|
|
32
|
+
formatDiscoveryForVerification,
|
|
33
|
+
getManualWiringQuestionnaire,
|
|
34
|
+
createWiringFromManualAnswers,
|
|
35
|
+
resolveWiringFromVerification,
|
|
36
|
+
} from '../utils/host-app-discovery.js';
|
|
37
|
+
import path from 'path';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Masks API key for display (shows first 8 chars + ...)
|
|
41
|
+
*/
|
|
42
|
+
function maskApiKey(apiKey) {
|
|
43
|
+
if (!apiKey || apiKey.length < 8) return '***';
|
|
44
|
+
return `${apiKey.substring(0, 8)}...`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Unified Velt Installer - Main Entry Point
|
|
49
|
+
*
|
|
50
|
+
* @param {Object} params - Installation parameters
|
|
51
|
+
* @param {string} params.projectPath - Path to project (REQUIRED)
|
|
52
|
+
* @param {string} params.apiKey - Velt API key (REQUIRED)
|
|
53
|
+
* @param {string} params.authToken - Velt Auth Token (REQUIRED)
|
|
54
|
+
* @param {string} [params.mode='guided'] - Installation mode: 'guided' | 'cli-only'
|
|
55
|
+
* @param {string} [params.stage='plan'] - Guided mode stage: 'plan' | 'apply'
|
|
56
|
+
* @param {boolean} [params.approved=false] - Whether user approved the plan (for apply stage)
|
|
57
|
+
* @param {string[]} [params.features=['comments']] - Features to install
|
|
58
|
+
* @param {string} [params.commentType='freestyle'] - Comment type
|
|
59
|
+
* @param {string} [params.crdtEditorType=null] - CRDT editor type
|
|
60
|
+
* @param {string} [params.headerPosition='top-right'] - Sidebar header position
|
|
61
|
+
* @param {string} [params.veltProviderLocation='app/page.tsx'] - VeltProvider location
|
|
62
|
+
* @param {string} [params.discoveryConsent] - User consent for codebase scanning: 'yes' | 'no'
|
|
63
|
+
* @param {Object} [params.discoveryVerification] - Verification of scan results
|
|
64
|
+
* @param {Object} [params.manualWiring] - Manual wiring answers (if consent='no')
|
|
65
|
+
* @param {Object} [params.server=null] - MCP server instance
|
|
66
|
+
* @returns {Promise<Object>} Installation result with status field
|
|
67
|
+
*/
|
|
68
|
+
export async function installVeltUnified(params) {
|
|
69
|
+
const {
|
|
70
|
+
projectPath,
|
|
71
|
+
apiKey,
|
|
72
|
+
authToken,
|
|
73
|
+
mode = 'guided',
|
|
74
|
+
stage = 'plan',
|
|
75
|
+
approved = false,
|
|
76
|
+
features = ['comments'],
|
|
77
|
+
commentType = 'freestyle',
|
|
78
|
+
crdtEditorType = null,
|
|
79
|
+
headerPosition = 'top-right',
|
|
80
|
+
veltProviderLocation = 'app/page.tsx',
|
|
81
|
+
discoveryConsent, // NEW: 'yes' | 'no' | undefined
|
|
82
|
+
discoveryVerification, // NEW: { status, overrides? }
|
|
83
|
+
manualWiring, // NEW: { documentId, user, auth, insertion }
|
|
84
|
+
server = null,
|
|
85
|
+
} = params;
|
|
86
|
+
|
|
87
|
+
const resolvedPath = path.resolve(projectPath);
|
|
88
|
+
|
|
89
|
+
// Validate required parameters
|
|
90
|
+
if (!apiKey) {
|
|
91
|
+
return {
|
|
92
|
+
status: 'error',
|
|
93
|
+
error: 'API key is required. Please provide your Velt API Key from https://console.velt.dev',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!authToken) {
|
|
97
|
+
return {
|
|
98
|
+
status: 'error',
|
|
99
|
+
error: 'Auth token is required. Please provide your Velt Auth Token from https://console.velt.dev',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Step 1: Validate Next.js project
|
|
104
|
+
console.error('\nš Validating project...');
|
|
105
|
+
const projectValidation = validateNextJsProject(resolvedPath);
|
|
106
|
+
if (!projectValidation.valid) {
|
|
107
|
+
return {
|
|
108
|
+
status: 'error',
|
|
109
|
+
error: projectValidation.error,
|
|
110
|
+
hint: 'Make sure you are in a Next.js project directory with a package.json that includes "next" as a dependency.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
console.error('ā
Valid Next.js project\n');
|
|
114
|
+
|
|
115
|
+
// Step 2: Detect framework type
|
|
116
|
+
console.error('š Detecting framework...');
|
|
117
|
+
const frameworkInfo = getFrameworkInfo(resolvedPath);
|
|
118
|
+
console.error(` Project type: ${frameworkInfo.projectType}`);
|
|
119
|
+
console.error(` Needs "use client": ${frameworkInfo.needsUseClient}`);
|
|
120
|
+
if (frameworkInfo.routerType) {
|
|
121
|
+
console.error(` Router type: ${frameworkInfo.routerType}`);
|
|
122
|
+
}
|
|
123
|
+
console.error('');
|
|
124
|
+
|
|
125
|
+
// Step 3: Route based on mode
|
|
126
|
+
if (mode === 'cli-only') {
|
|
127
|
+
console.error('š¦ CLI-Only Mode (SKIP)\n');
|
|
128
|
+
return runCliOnlyInstall({
|
|
129
|
+
projectPath: resolvedPath,
|
|
130
|
+
apiKey,
|
|
131
|
+
authToken,
|
|
132
|
+
frameworkInfo,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Step 4: Guided mode - route based on stage
|
|
137
|
+
if (mode === 'guided') {
|
|
138
|
+
if (stage === 'plan') {
|
|
139
|
+
console.error('š Guided Mode - Plan Stage\n');
|
|
140
|
+
return runGuidedPlanStage({
|
|
141
|
+
projectPath: resolvedPath,
|
|
142
|
+
apiKey,
|
|
143
|
+
authToken,
|
|
144
|
+
features,
|
|
145
|
+
commentType,
|
|
146
|
+
crdtEditorType,
|
|
147
|
+
headerPosition,
|
|
148
|
+
veltProviderLocation,
|
|
149
|
+
frameworkInfo,
|
|
150
|
+
// NEW: Pass discovery-related params
|
|
151
|
+
discoveryConsent,
|
|
152
|
+
discoveryVerification,
|
|
153
|
+
manualWiring,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (stage === 'apply') {
|
|
158
|
+
if (!approved) {
|
|
159
|
+
return {
|
|
160
|
+
status: 'error',
|
|
161
|
+
error: 'Cannot apply without user approval. Set approved=true after user confirms the plan.',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
console.error('š Guided Mode - Apply Stage\n');
|
|
165
|
+
return runGuidedApplyStage({
|
|
166
|
+
projectPath: resolvedPath,
|
|
167
|
+
apiKey,
|
|
168
|
+
authToken,
|
|
169
|
+
features,
|
|
170
|
+
commentType,
|
|
171
|
+
crdtEditorType,
|
|
172
|
+
headerPosition,
|
|
173
|
+
veltProviderLocation,
|
|
174
|
+
frameworkInfo,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
status: 'error',
|
|
181
|
+
error: `Invalid mode "${mode}" or stage "${stage}". Use mode="guided"|"cli-only" and stage="plan"|"apply".`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* CLI-Only Installation (SKIP path)
|
|
187
|
+
*
|
|
188
|
+
* Runs Velt CLI scaffolding only (NO feature flags = core only),
|
|
189
|
+
* validates basic installation, applies "use client" fixes for Next.js,
|
|
190
|
+
* returns TODO checklist. Does NOT modify project files beyond CLI.
|
|
191
|
+
*
|
|
192
|
+
* @param {Object} params
|
|
193
|
+
* @param {string} params.projectPath - Path to project
|
|
194
|
+
* @param {string} params.apiKey - Velt API key
|
|
195
|
+
* @param {string} params.authToken - Velt Auth Token
|
|
196
|
+
* @param {Object} params.frameworkInfo - Framework detection info
|
|
197
|
+
* @returns {Promise<Object>} CLI-only installation result
|
|
198
|
+
*/
|
|
199
|
+
export async function runCliOnlyInstall({ projectPath, apiKey, authToken, frameworkInfo }) {
|
|
200
|
+
const report = {
|
|
201
|
+
status: 'cli_only_complete',
|
|
202
|
+
mode: 'cli-only',
|
|
203
|
+
steps: [],
|
|
204
|
+
startTime: new Date().toISOString(),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
// Step 1: Run Velt CLI (core only, no feature flags)
|
|
209
|
+
console.error('āļø Running Velt CLI (core scaffolding only)...');
|
|
210
|
+
const cliResult = await runVeltCliCoreOnly({
|
|
211
|
+
projectPath,
|
|
212
|
+
apiKey,
|
|
213
|
+
authToken,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
report.steps.push({
|
|
217
|
+
step: 1,
|
|
218
|
+
name: 'run_velt_cli',
|
|
219
|
+
status: cliResult.success ? 'complete' : 'complete_with_warnings',
|
|
220
|
+
result: {
|
|
221
|
+
exitCode: cliResult.exitCode,
|
|
222
|
+
method: cliResult.method, // 'linked' or 'direct'
|
|
223
|
+
command: cliResult.command,
|
|
224
|
+
warning: cliResult.success ? null : 'CLI had issues but may have created files',
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (cliResult.success) {
|
|
229
|
+
console.error(`ā
Velt CLI completed (via ${cliResult.method})\n`);
|
|
230
|
+
} else {
|
|
231
|
+
console.error(`ā ļø Velt CLI completed with warnings (via ${cliResult.method})\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Step 2: Apply "use client" fixes for Next.js
|
|
235
|
+
if (frameworkInfo.needsUseClient) {
|
|
236
|
+
console.error('š§ Applying "use client" directives for Next.js...');
|
|
237
|
+
const useClientResult = applyFixes(projectPath);
|
|
238
|
+
|
|
239
|
+
report.steps.push({
|
|
240
|
+
step: 2,
|
|
241
|
+
name: 'apply_use_client',
|
|
242
|
+
status: 'complete',
|
|
243
|
+
result: {
|
|
244
|
+
filesFixed: useClientResult.fixed?.length || 0,
|
|
245
|
+
filesScanned: useClientResult.scanned?.length || 0,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (useClientResult.fixed?.length > 0) {
|
|
250
|
+
console.error(` Fixed ${useClientResult.fixed.length} file(s)`);
|
|
251
|
+
for (const fix of useClientResult.fixed) {
|
|
252
|
+
console.error(` - ${fix.path}`);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
console.error(' No files needed fixing');
|
|
256
|
+
}
|
|
257
|
+
console.error('');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Step 3: Run basic QA (no integration checks)
|
|
261
|
+
console.error('š Running basic CLI validation...');
|
|
262
|
+
const qaResult = await validateBasicCliInstall({
|
|
263
|
+
projectPath,
|
|
264
|
+
cliResult, // Pass CLI result for method reporting
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
report.steps.push({
|
|
268
|
+
step: frameworkInfo.needsUseClient ? 3 : 2,
|
|
269
|
+
name: 'basic_qa_validation',
|
|
270
|
+
status: 'complete',
|
|
271
|
+
result: qaResult,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
console.error(`ā
Validation complete: ${qaResult.score}\n`);
|
|
275
|
+
|
|
276
|
+
// Step 4: Generate CLI-only report
|
|
277
|
+
console.error('š Generating installation report...\n');
|
|
278
|
+
const cliReport = createCliOnlyReport({
|
|
279
|
+
cliResult,
|
|
280
|
+
qaResult,
|
|
281
|
+
apiKey: maskApiKey(apiKey),
|
|
282
|
+
cliMethod: cliResult.method, // Add CLI method to report
|
|
283
|
+
frameworkInfo,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
report.report = cliReport;
|
|
287
|
+
report.validation = qaResult;
|
|
288
|
+
report.endTime = new Date().toISOString();
|
|
289
|
+
report.cliMethod = cliResult.method;
|
|
290
|
+
report.frameworkInfo = {
|
|
291
|
+
projectType: frameworkInfo.projectType,
|
|
292
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
293
|
+
};
|
|
294
|
+
report.nextSteps = 'Re-run installer without SKIP to generate a plan + implement features';
|
|
295
|
+
|
|
296
|
+
return report;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error(`\nā Error: ${error.message}\n`);
|
|
299
|
+
report.status = 'failed';
|
|
300
|
+
report.endTime = new Date().toISOString();
|
|
301
|
+
report.error = {
|
|
302
|
+
message: error.message,
|
|
303
|
+
stack: error.stack,
|
|
304
|
+
};
|
|
305
|
+
return report;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Guided Plan Stage
|
|
311
|
+
*
|
|
312
|
+
* Multi-step flow with discovery consent and verification:
|
|
313
|
+
* 1. Run CLI with feature flags
|
|
314
|
+
* 2. Scan codebase
|
|
315
|
+
* 3. Check discovery consent:
|
|
316
|
+
* - No consent ā return awaiting_discovery_consent
|
|
317
|
+
* - consent=yes, no verification ā run scan, return awaiting_discovery_verification
|
|
318
|
+
* - consent=yes, verification provided ā use verified/overridden results
|
|
319
|
+
* - consent=no, no manualWiring ā return awaiting_manual_wiring_answers
|
|
320
|
+
* - consent=no, manualWiring provided ā use manual answers
|
|
321
|
+
* 4. Fetch docs
|
|
322
|
+
* 5. Generate plan with wiring data
|
|
323
|
+
*
|
|
324
|
+
* @param {Object} params
|
|
325
|
+
* @param {string} params.projectPath - Path to project
|
|
326
|
+
* @param {string} params.apiKey - Velt API key
|
|
327
|
+
* @param {string} params.authToken - Velt Auth Token
|
|
328
|
+
* @param {string[]} params.features - Features to install
|
|
329
|
+
* @param {string} params.commentType - Comment type
|
|
330
|
+
* @param {string} params.crdtEditorType - CRDT editor type
|
|
331
|
+
* @param {string} params.headerPosition - Header position
|
|
332
|
+
* @param {string} params.veltProviderLocation - VeltProvider location
|
|
333
|
+
* @param {Object} params.frameworkInfo - Framework detection info
|
|
334
|
+
* @param {string} [params.discoveryConsent] - 'yes' | 'no' | undefined
|
|
335
|
+
* @param {Object} [params.discoveryVerification] - { status, overrides? }
|
|
336
|
+
* @param {Object} [params.manualWiring] - { documentId, user, auth, insertion }
|
|
337
|
+
* @returns {Promise<Object>} Plan generation result with status field
|
|
338
|
+
*/
|
|
339
|
+
export async function runGuidedPlanStage({
|
|
340
|
+
projectPath,
|
|
341
|
+
apiKey,
|
|
342
|
+
authToken,
|
|
343
|
+
features,
|
|
344
|
+
commentType,
|
|
345
|
+
crdtEditorType,
|
|
346
|
+
headerPosition,
|
|
347
|
+
veltProviderLocation,
|
|
348
|
+
frameworkInfo,
|
|
349
|
+
discoveryConsent,
|
|
350
|
+
discoveryVerification,
|
|
351
|
+
manualWiring,
|
|
352
|
+
}) {
|
|
353
|
+
const report = {
|
|
354
|
+
status: 'in_progress',
|
|
355
|
+
mode: 'guided',
|
|
356
|
+
stage: 'plan',
|
|
357
|
+
steps: [],
|
|
358
|
+
startTime: new Date().toISOString(),
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
console.error('š Starting Guided Installation - Plan Stage');
|
|
363
|
+
console.error(`š Project: ${projectPath}`);
|
|
364
|
+
console.error(`š API Key: ${maskApiKey(apiKey)}`);
|
|
365
|
+
console.error(`⨠Features: ${features.join(', ')}`);
|
|
366
|
+
console.error(`š„ļø Framework: ${frameworkInfo.projectType}`);
|
|
367
|
+
if (features.includes('comments')) {
|
|
368
|
+
console.error(`š¬ Comment Type: ${commentType}`);
|
|
369
|
+
}
|
|
370
|
+
if (features.includes('crdt') && crdtEditorType) {
|
|
371
|
+
console.error(`š CRDT Editor: ${crdtEditorType}`);
|
|
372
|
+
}
|
|
373
|
+
console.error('');
|
|
374
|
+
|
|
375
|
+
// Step 1: Run Velt CLI with feature flags
|
|
376
|
+
console.error('āļø Step 1/5: Running Velt CLI with feature flags...');
|
|
377
|
+
const cliResult = await runVeltCliWithFeatures({
|
|
378
|
+
projectPath,
|
|
379
|
+
apiKey,
|
|
380
|
+
authToken,
|
|
381
|
+
features,
|
|
382
|
+
commentType,
|
|
383
|
+
crdtEditorType,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
report.steps.push({
|
|
387
|
+
step: 1,
|
|
388
|
+
name: 'run_velt_cli',
|
|
389
|
+
status: cliResult.success ? 'complete' : 'complete_with_warnings',
|
|
390
|
+
result: {
|
|
391
|
+
exitCode: cliResult.exitCode,
|
|
392
|
+
method: cliResult.method,
|
|
393
|
+
command: cliResult.command,
|
|
394
|
+
warning: cliResult.success ? null : 'CLI had issues but may have created files',
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
if (cliResult.success) {
|
|
399
|
+
console.error(`ā
Step 1/5: Velt CLI completed (via ${cliResult.method})\n`);
|
|
400
|
+
} else {
|
|
401
|
+
console.error(`ā ļø Step 1/5: Velt CLI completed with warnings (via ${cliResult.method})\n`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Step 1.5: Apply "use client" fixes for Next.js
|
|
405
|
+
if (frameworkInfo.needsUseClient) {
|
|
406
|
+
console.error('š§ Applying "use client" directives...');
|
|
407
|
+
const useClientResult = applyFixes(projectPath);
|
|
408
|
+
if (useClientResult.fixed?.length > 0) {
|
|
409
|
+
console.error(` Fixed ${useClientResult.fixed.length} file(s)\n`);
|
|
410
|
+
} else {
|
|
411
|
+
console.error(' No files needed fixing\n');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Step 2: Scan codebase (detect libraries)
|
|
416
|
+
console.error('š Step 2/5: Scanning codebase...');
|
|
417
|
+
const libraryDetection = detectLibraries(projectPath);
|
|
418
|
+
const detectedLibs = Object.entries(libraryDetection)
|
|
419
|
+
.filter(([_, detected]) => detected)
|
|
420
|
+
.map(([key]) => key);
|
|
421
|
+
|
|
422
|
+
if (detectedLibs.length > 0) {
|
|
423
|
+
console.error(` š Detected libraries: ${detectedLibs.join(', ')}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
report.steps.push({
|
|
427
|
+
step: 2,
|
|
428
|
+
name: 'scan_codebase',
|
|
429
|
+
status: 'complete',
|
|
430
|
+
result: {
|
|
431
|
+
librariesDetected: detectedLibs,
|
|
432
|
+
frameworkInfo: {
|
|
433
|
+
projectType: frameworkInfo.projectType,
|
|
434
|
+
routerType: frameworkInfo.routerType,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
console.error('ā
Step 2/5: Codebase scanned\n');
|
|
440
|
+
|
|
441
|
+
// ============================================================
|
|
442
|
+
// Step 3: Discovery Consent Gate
|
|
443
|
+
// ============================================================
|
|
444
|
+
|
|
445
|
+
// 3A: No consent provided yet ā ask for consent
|
|
446
|
+
if (!discoveryConsent) {
|
|
447
|
+
console.error('š Step 3/5: Awaiting discovery consent...\n');
|
|
448
|
+
report.status = 'awaiting_discovery_consent';
|
|
449
|
+
report.cliMethod = cliResult.method;
|
|
450
|
+
report.cliResult = {
|
|
451
|
+
success: cliResult.success,
|
|
452
|
+
method: cliResult.method,
|
|
453
|
+
command: cliResult.command,
|
|
454
|
+
};
|
|
455
|
+
report.frameworkInfo = {
|
|
456
|
+
projectType: frameworkInfo.projectType,
|
|
457
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
458
|
+
};
|
|
459
|
+
report.message = 'CLI scaffolding complete. Ready to discover integration points.';
|
|
460
|
+
report.nextAction = {
|
|
461
|
+
question: 'Do you want me to scan your codebase to infer Document ID, User identity, setDocuments placement, and JWT/Auth wiring?',
|
|
462
|
+
options: [
|
|
463
|
+
{ value: 'yes', label: 'YES (recommended) - Scan codebase automatically' },
|
|
464
|
+
{ value: 'no', label: 'NO - I\'ll provide the wiring info manually' },
|
|
465
|
+
],
|
|
466
|
+
};
|
|
467
|
+
return report;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// 3B: Consent = YES ā scan path
|
|
471
|
+
let wiring = null;
|
|
472
|
+
let discoveryResult = null;
|
|
473
|
+
|
|
474
|
+
if (discoveryConsent === 'yes') {
|
|
475
|
+
console.error('š Step 3/5: Running host app wiring discovery...');
|
|
476
|
+
discoveryResult = discoverHostAppWiring(projectPath);
|
|
477
|
+
|
|
478
|
+
report.steps.push({
|
|
479
|
+
step: 3,
|
|
480
|
+
name: 'host_app_discovery',
|
|
481
|
+
status: 'complete',
|
|
482
|
+
result: {
|
|
483
|
+
totalSignals: discoveryResult.summary.totalSignals,
|
|
484
|
+
questionsCount: discoveryResult.summary.questionsForDeveloper.length,
|
|
485
|
+
documentIdConfidence: discoveryResult.documentId.confidence,
|
|
486
|
+
userAuthConfidence: discoveryResult.user.confidence,
|
|
487
|
+
authProvider: discoveryResult.user.authProvider,
|
|
488
|
+
recommendedSetupLocation: discoveryResult.setDocuments.recommendedLocation?.file,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
console.error(` Found ${discoveryResult.summary.totalSignals} signals\n`);
|
|
493
|
+
|
|
494
|
+
// 3B-i: Scan done but no verification yet ā ask for verification
|
|
495
|
+
if (!discoveryVerification) {
|
|
496
|
+
console.error(' āøļø Awaiting user verification of scan results...\n');
|
|
497
|
+
report.status = 'awaiting_discovery_verification';
|
|
498
|
+
report.discovery = discoveryResult;
|
|
499
|
+
report.formattedFindings = formatDiscoveryForVerification(discoveryResult);
|
|
500
|
+
report.cliMethod = cliResult.method;
|
|
501
|
+
report.frameworkInfo = {
|
|
502
|
+
projectType: frameworkInfo.projectType,
|
|
503
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
504
|
+
};
|
|
505
|
+
report.message = 'Discovery scan complete. Please verify the findings.';
|
|
506
|
+
report.nextAction = {
|
|
507
|
+
question: 'Are these findings correct?',
|
|
508
|
+
options: [
|
|
509
|
+
{ value: 'confirmed', label: 'CONFIRM ALL - Findings are correct' },
|
|
510
|
+
{ value: 'edited', label: 'EDIT - I need to correct some items' },
|
|
511
|
+
{ value: 'unsure', label: 'UNSURE - Need human help to determine this' },
|
|
512
|
+
],
|
|
513
|
+
};
|
|
514
|
+
return report;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// 3B-ii: Verification provided ā resolve wiring
|
|
518
|
+
console.error(' ā
Verification received, resolving wiring...\n');
|
|
519
|
+
wiring = resolveWiringFromVerification(discoveryResult, discoveryVerification);
|
|
520
|
+
|
|
521
|
+
} else if (discoveryConsent === 'no') {
|
|
522
|
+
// 3C: Consent = NO ā manual path
|
|
523
|
+
console.error('š Step 3/5: Manual wiring path (user declined scanning)...');
|
|
524
|
+
|
|
525
|
+
// 3C-i: No manual wiring yet ā ask questionnaire
|
|
526
|
+
if (!manualWiring) {
|
|
527
|
+
console.error(' āøļø Awaiting manual wiring answers...\n');
|
|
528
|
+
report.status = 'awaiting_manual_wiring_answers';
|
|
529
|
+
report.questionnaire = getManualWiringQuestionnaire();
|
|
530
|
+
report.cliMethod = cliResult.method;
|
|
531
|
+
report.frameworkInfo = {
|
|
532
|
+
projectType: frameworkInfo.projectType,
|
|
533
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
534
|
+
};
|
|
535
|
+
report.message = 'Please provide wiring information manually.';
|
|
536
|
+
return report;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// 3C-ii: Manual wiring provided ā use it
|
|
540
|
+
console.error(' ā
Manual wiring received, processing...\n');
|
|
541
|
+
wiring = createWiringFromManualAnswers(manualWiring);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// At this point we MUST have wiring data
|
|
545
|
+
if (!wiring) {
|
|
546
|
+
throw new Error('Internal error: wiring data not resolved after discovery/manual flow');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
report.steps.push({
|
|
550
|
+
step: 3.5,
|
|
551
|
+
name: 'wiring_resolved',
|
|
552
|
+
status: 'complete',
|
|
553
|
+
result: {
|
|
554
|
+
source: wiring.source,
|
|
555
|
+
hasTodos: wiring.todos.length > 0,
|
|
556
|
+
todoCount: wiring.todos.length,
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
if (wiring.todos.length > 0) {
|
|
561
|
+
console.error(` ā ļø ${wiring.todos.length} item(s) marked as TODO (need developer input)\n`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// ============================================================
|
|
565
|
+
// Step 4: Fetch implementation docs (parallel)
|
|
566
|
+
// ============================================================
|
|
567
|
+
console.error('š Step 4/5: Fetching implementation details from Velt Docs...');
|
|
568
|
+
|
|
569
|
+
const fetchPromises = [];
|
|
570
|
+
const fetchResults = {};
|
|
571
|
+
|
|
572
|
+
// Fetch comments implementation
|
|
573
|
+
if (features.includes('comments')) {
|
|
574
|
+
fetchPromises.push(
|
|
575
|
+
fetchCommentImplementation({ commentType, mcpClient: null })
|
|
576
|
+
.then(result => { fetchResults.comments = result; })
|
|
577
|
+
.catch(err => { fetchResults.comments = { error: err.message }; })
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Fetch CRDT implementation
|
|
582
|
+
if (features.includes('crdt') && !crdtEditorType) {
|
|
583
|
+
console.error(' ā ļø CRDT feature selected but no crdtEditorType provided ā skipping CRDT docs fetch');
|
|
584
|
+
}
|
|
585
|
+
if (features.includes('crdt') && crdtEditorType) {
|
|
586
|
+
fetchPromises.push(
|
|
587
|
+
fetchCrdtImplementation({ editorType: crdtEditorType, mcpClient: null })
|
|
588
|
+
.then(result => { fetchResults.crdt = result; })
|
|
589
|
+
.catch(err => { fetchResults.crdt = { error: err.message }; })
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Fetch other feature implementations
|
|
594
|
+
for (const feature of features) {
|
|
595
|
+
if (feature !== 'comments' && feature !== 'crdt') {
|
|
596
|
+
fetchPromises.push(
|
|
597
|
+
fetchFeatureImplementation({ feature, mcpClient: null })
|
|
598
|
+
.then(result => { fetchResults[feature] = result; })
|
|
599
|
+
.catch(err => { fetchResults[feature] = { error: err.message }; })
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
await Promise.all(fetchPromises);
|
|
605
|
+
|
|
606
|
+
// Extract results ā filter out error objects so they don't pass as valid implementations
|
|
607
|
+
const isValidResult = (r) => r && !r.error;
|
|
608
|
+
|
|
609
|
+
const implementation = isValidResult(fetchResults.comments) ? fetchResults.comments : null;
|
|
610
|
+
const crdtImplementation = isValidResult(fetchResults.crdt) ? fetchResults.crdt : null;
|
|
611
|
+
const featureImplementations = {};
|
|
612
|
+
|
|
613
|
+
for (const [key, value] of Object.entries(fetchResults)) {
|
|
614
|
+
if (key !== 'comments' && key !== 'crdt') {
|
|
615
|
+
if (isValidResult(value)) {
|
|
616
|
+
featureImplementations[key] = value;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Log results
|
|
622
|
+
if (implementation) {
|
|
623
|
+
console.error(` ā
Got ${commentType} comments from: ${implementation.source || 'docs'}`);
|
|
624
|
+
} else if (fetchResults.comments?.error) {
|
|
625
|
+
console.error(` ā ļø Failed to fetch ${commentType} comments: ${fetchResults.comments.error}`);
|
|
626
|
+
}
|
|
627
|
+
if (crdtImplementation) {
|
|
628
|
+
console.error(` ā
Got ${crdtEditorType} CRDT from: ${crdtImplementation.source || 'docs'}`);
|
|
629
|
+
} else if (fetchResults.crdt?.error) {
|
|
630
|
+
console.error(` ā ļø Failed to fetch ${crdtEditorType} CRDT: ${fetchResults.crdt.error}`);
|
|
631
|
+
}
|
|
632
|
+
for (const [feature, impl] of Object.entries(featureImplementations)) {
|
|
633
|
+
console.error(` ā
Got ${feature} from: ${impl.source || 'docs'}`);
|
|
634
|
+
}
|
|
635
|
+
// Log failed feature fetches
|
|
636
|
+
for (const [key, value] of Object.entries(fetchResults)) {
|
|
637
|
+
if (key !== 'comments' && key !== 'crdt' && value?.error) {
|
|
638
|
+
console.error(` ā ļø Failed to fetch ${key}: ${value.error}`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
report.steps.push({
|
|
643
|
+
step: 4,
|
|
644
|
+
name: 'fetch_implementation',
|
|
645
|
+
status: 'complete',
|
|
646
|
+
result: {
|
|
647
|
+
comments: implementation ? { source: implementation.source, docUrl: implementation.docUrl } : null,
|
|
648
|
+
crdt: crdtImplementation ? { source: crdtImplementation.source, docUrl: crdtImplementation.docUrl } : null,
|
|
649
|
+
otherFeatures: Object.keys(featureImplementations).length,
|
|
650
|
+
},
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
console.error('ā
Step 4/5: Implementation details fetched\n');
|
|
654
|
+
|
|
655
|
+
// ============================================================
|
|
656
|
+
// Step 5: Generate plan with wiring data
|
|
657
|
+
// ============================================================
|
|
658
|
+
console.error('š Step 5/5: Generating implementation plan...');
|
|
659
|
+
|
|
660
|
+
const plan = features.length > 1 || (features.length === 1 && features[0] !== 'comments')
|
|
661
|
+
? createMultiFeaturePlan({
|
|
662
|
+
features,
|
|
663
|
+
commentType,
|
|
664
|
+
implementation,
|
|
665
|
+
crdtImplementation,
|
|
666
|
+
featureImplementations,
|
|
667
|
+
detectedFiles: [],
|
|
668
|
+
apiKey: maskApiKey(apiKey),
|
|
669
|
+
headerPosition,
|
|
670
|
+
veltProviderLocation,
|
|
671
|
+
crdtEditorType,
|
|
672
|
+
frameworkInfo,
|
|
673
|
+
wiring, // Pass resolved wiring data
|
|
674
|
+
})
|
|
675
|
+
: createVeltCommentsPlan({
|
|
676
|
+
commentType,
|
|
677
|
+
implementation,
|
|
678
|
+
detectedFiles: [],
|
|
679
|
+
apiKey: maskApiKey(apiKey),
|
|
680
|
+
headerPosition,
|
|
681
|
+
veltProviderLocation,
|
|
682
|
+
crdtEditorType,
|
|
683
|
+
frameworkInfo,
|
|
684
|
+
wiring, // Pass resolved wiring data
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
// Append wiring section to the plan
|
|
688
|
+
let wiringSection = `\n## š Host App Wiring (${wiring.source})\n\n`;
|
|
689
|
+
|
|
690
|
+
if (wiring.documentId && !wiring.documentId.unsure) {
|
|
691
|
+
wiringSection += `### Document ID\n`;
|
|
692
|
+
if (wiring.documentId.method) wiringSection += `- **Method**: ${wiring.documentId.method}\n`;
|
|
693
|
+
if (wiring.documentId.filePath) wiringSection += `- **File**: \`${wiring.documentId.filePath}\`\n`;
|
|
694
|
+
if (wiring.documentId.variableName) wiringSection += `- **Variable**: \`${wiring.documentId.variableName}\`\n`;
|
|
695
|
+
wiringSection += '\n';
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (wiring.user && !wiring.user.unsure) {
|
|
699
|
+
wiringSection += `### User Authentication\n`;
|
|
700
|
+
if (wiring.user.providerType) wiringSection += `- **Provider**: ${wiring.user.providerType}\n`;
|
|
701
|
+
if (wiring.user.filePath) wiringSection += `- **File**: \`${wiring.user.filePath}\`\n`;
|
|
702
|
+
if (wiring.user.fields) wiringSection += `- **Fields**: ${wiring.user.fields.join(', ')}\n`;
|
|
703
|
+
wiringSection += '\n';
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (wiring.insertion && !wiring.insertion.unsure) {
|
|
707
|
+
wiringSection += `### Velt Initialization Location\n`;
|
|
708
|
+
if (wiring.insertion.locationType) wiringSection += `- **Location type**: ${wiring.insertion.locationType}\n`;
|
|
709
|
+
if (wiring.insertion.filePath) wiringSection += `- **File**: \`${wiring.insertion.filePath}\`\n`;
|
|
710
|
+
wiringSection += '\n';
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (wiring.todos.length > 0) {
|
|
714
|
+
wiringSection += `### ā ļø TODOs (Need Developer Input)\n\n`;
|
|
715
|
+
wiringSection += wiring.todos.map(t => `- ${t}`).join('\n') + '\n\n';
|
|
716
|
+
wiringSection += `**IMPORTANT**: The items above need human clarification before implementation.\n`;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const planWithWiring = plan + wiringSection;
|
|
720
|
+
|
|
721
|
+
report.steps.push({
|
|
722
|
+
step: 5,
|
|
723
|
+
name: 'generate_plan',
|
|
724
|
+
status: 'complete',
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
report.status = 'plan_generated';
|
|
728
|
+
report.plan = planWithWiring;
|
|
729
|
+
report.wiring = wiring;
|
|
730
|
+
report.endTime = new Date().toISOString();
|
|
731
|
+
report.cliMethod = cliResult.method;
|
|
732
|
+
report.frameworkInfo = {
|
|
733
|
+
projectType: frameworkInfo.projectType,
|
|
734
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
735
|
+
};
|
|
736
|
+
report.message = 'Plan generated. Present to user and await approval before applying.';
|
|
737
|
+
|
|
738
|
+
console.error('ā
Step 5/5: Plan generated\n');
|
|
739
|
+
console.error('š Present the plan to the user and ask for approval.\n');
|
|
740
|
+
|
|
741
|
+
if (wiring.todos.length > 0) {
|
|
742
|
+
console.error('ā ļø IMPORTANT: The plan includes TODOs that need developer input.');
|
|
743
|
+
console.error(' Please review the "TODOs" section before proceeding.\n');
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return report;
|
|
747
|
+
} catch (error) {
|
|
748
|
+
console.error(`\nā Error: ${error.message}\n`);
|
|
749
|
+
report.status = 'error';
|
|
750
|
+
report.endTime = new Date().toISOString();
|
|
751
|
+
report.error = {
|
|
752
|
+
message: error.message,
|
|
753
|
+
stack: error.stack,
|
|
754
|
+
};
|
|
755
|
+
return report;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Guided Apply Stage
|
|
761
|
+
*
|
|
762
|
+
* Called after user approves the plan. The AI is expected to apply
|
|
763
|
+
* the plan by making file edits. This function runs full QA validation
|
|
764
|
+
* and ensures "use client" directives are applied for Next.js.
|
|
765
|
+
*
|
|
766
|
+
* Note: The actual file edits are done by the AI based on the plan,
|
|
767
|
+
* not by this function. This function validates the result.
|
|
768
|
+
*
|
|
769
|
+
* @param {Object} params
|
|
770
|
+
* @param {string} params.projectPath - Path to project
|
|
771
|
+
* @param {string} params.apiKey - Velt API key
|
|
772
|
+
* @param {string} params.authToken - Velt Auth Token
|
|
773
|
+
* @param {string[]} params.features - Features installed
|
|
774
|
+
* @param {string} params.commentType - Comment type
|
|
775
|
+
* @param {string} params.crdtEditorType - CRDT editor type
|
|
776
|
+
* @param {string} params.headerPosition - Header position
|
|
777
|
+
* @param {string} params.veltProviderLocation - VeltProvider location
|
|
778
|
+
* @param {Object} params.frameworkInfo - Framework detection info
|
|
779
|
+
* @returns {Promise<Object>} Apply stage result
|
|
780
|
+
*/
|
|
781
|
+
export async function runGuidedApplyStage({
|
|
782
|
+
projectPath,
|
|
783
|
+
apiKey,
|
|
784
|
+
authToken,
|
|
785
|
+
features,
|
|
786
|
+
commentType,
|
|
787
|
+
crdtEditorType,
|
|
788
|
+
headerPosition,
|
|
789
|
+
veltProviderLocation,
|
|
790
|
+
frameworkInfo,
|
|
791
|
+
}) {
|
|
792
|
+
const report = {
|
|
793
|
+
status: 'apply_complete',
|
|
794
|
+
mode: 'guided',
|
|
795
|
+
stage: 'apply',
|
|
796
|
+
steps: [],
|
|
797
|
+
startTime: new Date().toISOString(),
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
try {
|
|
801
|
+
console.error('š Guided Installation - Apply Stage');
|
|
802
|
+
console.error('š AI should have applied the plan. Running validation...\n');
|
|
803
|
+
|
|
804
|
+
// Step 1: Apply "use client" fixes for any files the AI may have created
|
|
805
|
+
if (frameworkInfo.needsUseClient) {
|
|
806
|
+
console.error('š§ Ensuring "use client" directives are applied...');
|
|
807
|
+
const useClientResult = applyFixes(projectPath);
|
|
808
|
+
|
|
809
|
+
report.steps.push({
|
|
810
|
+
step: 1,
|
|
811
|
+
name: 'apply_use_client',
|
|
812
|
+
status: 'complete',
|
|
813
|
+
result: {
|
|
814
|
+
filesFixed: useClientResult.fixed?.length || 0,
|
|
815
|
+
filesScanned: useClientResult.scanned?.length || 0,
|
|
816
|
+
},
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
if (useClientResult.fixed?.length > 0) {
|
|
820
|
+
console.error(` Fixed ${useClientResult.fixed.length} additional file(s)`);
|
|
821
|
+
for (const fix of useClientResult.fixed) {
|
|
822
|
+
console.error(` - ${fix.path}`);
|
|
823
|
+
}
|
|
824
|
+
} else {
|
|
825
|
+
console.error(' All files have correct directives');
|
|
826
|
+
}
|
|
827
|
+
console.error('');
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Step 2: Run full integration QA
|
|
831
|
+
console.error('š Running full integration validation...');
|
|
832
|
+
const qaResult = await validateInstallation({ projectPath });
|
|
833
|
+
|
|
834
|
+
report.steps.push({
|
|
835
|
+
step: frameworkInfo.needsUseClient ? 2 : 1,
|
|
836
|
+
name: 'full_qa_validation',
|
|
837
|
+
status: 'complete',
|
|
838
|
+
result: qaResult,
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
report.validation = qaResult;
|
|
842
|
+
report.endTime = new Date().toISOString();
|
|
843
|
+
report.frameworkInfo = {
|
|
844
|
+
projectType: frameworkInfo.projectType,
|
|
845
|
+
needsUseClient: frameworkInfo.needsUseClient,
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
console.error(`ā
Validation complete: ${qaResult.score}\n`);
|
|
849
|
+
|
|
850
|
+
// Generate summary message
|
|
851
|
+
const passedAll = qaResult.passed === qaResult.total;
|
|
852
|
+
if (passedAll) {
|
|
853
|
+
report.message = 'Installation complete! All validation checks passed. Check browser DevTools for Velt errors.';
|
|
854
|
+
console.error('š Installation complete! All checks passed.\n');
|
|
855
|
+
} else {
|
|
856
|
+
report.message = `Installation complete with some checks not passing (${qaResult.score}). Review the validation results and check browser DevTools for Velt errors.`;
|
|
857
|
+
console.error(`ā ļø Installation complete. Some checks may need attention: ${qaResult.score}\n`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Add guidance for next steps
|
|
861
|
+
report.nextSteps = [
|
|
862
|
+
'Start your development server (npm run dev)',
|
|
863
|
+
'Open browser DevTools Console (F12) and look for Velt messages',
|
|
864
|
+
'Test the installed features',
|
|
865
|
+
'If errors occur, query Velt Docs MCP for solutions',
|
|
866
|
+
];
|
|
867
|
+
|
|
868
|
+
return report;
|
|
869
|
+
} catch (error) {
|
|
870
|
+
console.error(`\nā Error: ${error.message}\n`);
|
|
871
|
+
report.status = 'failed';
|
|
872
|
+
report.endTime = new Date().toISOString();
|
|
873
|
+
report.error = {
|
|
874
|
+
message: error.message,
|
|
875
|
+
stack: error.stack,
|
|
876
|
+
};
|
|
877
|
+
return report;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export default {
|
|
882
|
+
installVeltUnified,
|
|
883
|
+
runCliOnlyInstall,
|
|
884
|
+
runGuidedPlanStage,
|
|
885
|
+
runGuidedApplyStage,
|
|
886
|
+
};
|