@l4yercak3/cli 1.3.1 → 2.0.0-alpha.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 +10 -220
- package/dist/api/client.d.ts +12 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +37 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/platform.d.ts +161 -0
- package/dist/api/platform.d.ts.map +1 -0
- package/dist/api/platform.js +70 -0
- package/dist/api/platform.js.map +1 -0
- package/dist/bin/sevenlayers.d.ts +3 -0
- package/dist/bin/sevenlayers.d.ts.map +1 -0
- package/dist/bin/sevenlayers.js +198 -0
- package/dist/bin/sevenlayers.js.map +1 -0
- package/dist/commands/agent/catalog.d.ts +5 -0
- package/dist/commands/agent/catalog.d.ts.map +1 -0
- package/dist/commands/agent/catalog.js +142 -0
- package/dist/commands/agent/catalog.js.map +1 -0
- package/dist/commands/agent/drift.d.ts +5 -0
- package/dist/commands/agent/drift.d.ts.map +1 -0
- package/dist/commands/agent/drift.js +113 -0
- package/dist/commands/agent/drift.js.map +1 -0
- package/dist/commands/agent/init.d.ts +5 -0
- package/dist/commands/agent/init.d.ts.map +1 -0
- package/dist/commands/agent/init.js +75 -0
- package/dist/commands/agent/init.js.map +1 -0
- package/dist/commands/agent/permissions.d.ts +5 -0
- package/dist/commands/agent/permissions.d.ts.map +1 -0
- package/dist/commands/agent/permissions.js +88 -0
- package/dist/commands/agent/permissions.js.map +1 -0
- package/dist/commands/agent/runner.d.ts +14 -0
- package/dist/commands/agent/runner.d.ts.map +1 -0
- package/dist/commands/agent/runner.js +59 -0
- package/dist/commands/agent/runner.js.map +1 -0
- package/dist/commands/agent/shared.d.ts +13 -0
- package/dist/commands/agent/shared.d.ts.map +1 -0
- package/dist/commands/agent/shared.js +31 -0
- package/dist/commands/agent/shared.js.map +1 -0
- package/dist/commands/agent/template.d.ts +5 -0
- package/dist/commands/agent/template.d.ts.map +1 -0
- package/dist/commands/agent/template.js +104 -0
- package/dist/commands/agent/template.js.map +1 -0
- package/dist/commands/app/connect.d.ts +7 -0
- package/dist/commands/app/connect.d.ts.map +1 -0
- package/dist/commands/app/connect.js +12 -0
- package/dist/commands/app/connect.js.map +1 -0
- package/dist/commands/app/init.d.ts +7 -0
- package/dist/commands/app/init.d.ts.map +1 -0
- package/dist/commands/app/init.js +12 -0
- package/dist/commands/app/init.js.map +1 -0
- package/dist/commands/app/link.d.ts +3 -0
- package/dist/commands/app/link.d.ts.map +1 -0
- package/dist/commands/app/link.js +92 -0
- package/dist/commands/app/link.js.map +1 -0
- package/dist/commands/app/pages.d.ts +15 -0
- package/dist/commands/app/pages.d.ts.map +1 -0
- package/dist/commands/app/pages.js +180 -0
- package/dist/commands/app/pages.js.map +1 -0
- package/dist/commands/app/register.d.ts +3 -0
- package/dist/commands/app/register.d.ts.map +1 -0
- package/dist/commands/app/register.js +120 -0
- package/dist/commands/app/register.js.map +1 -0
- package/dist/commands/app/remote.d.ts +14 -0
- package/dist/commands/app/remote.d.ts.map +1 -0
- package/dist/commands/app/remote.js +44 -0
- package/dist/commands/app/remote.js.map +1 -0
- package/dist/commands/app/setup.d.ts +3 -0
- package/dist/commands/app/setup.d.ts.map +1 -0
- package/dist/commands/app/setup.js +299 -0
- package/dist/commands/app/setup.js.map +1 -0
- package/dist/commands/app/shared.d.ts +9 -0
- package/dist/commands/app/shared.d.ts.map +1 -0
- package/dist/commands/app/shared.js +122 -0
- package/dist/commands/app/shared.js.map +1 -0
- package/dist/commands/app/sync.d.ts +7 -0
- package/dist/commands/app/sync.d.ts.map +1 -0
- package/dist/commands/app/sync.js +107 -0
- package/dist/commands/app/sync.js.map +1 -0
- package/dist/commands/booking/check.d.ts +3 -0
- package/dist/commands/booking/check.d.ts.map +1 -0
- package/dist/commands/booking/check.js +68 -0
- package/dist/commands/booking/check.js.map +1 -0
- package/dist/commands/booking/setup.d.ts +3 -0
- package/dist/commands/booking/setup.d.ts.map +1 -0
- package/dist/commands/booking/setup.js +95 -0
- package/dist/commands/booking/setup.js.map +1 -0
- package/dist/commands/booking/shared.d.ts +31 -0
- package/dist/commands/booking/shared.d.ts.map +1 -0
- package/dist/commands/booking/shared.js +112 -0
- package/dist/commands/booking/shared.js.map +1 -0
- package/dist/commands/booking/smoke.d.ts +3 -0
- package/dist/commands/booking/smoke.d.ts.map +1 -0
- package/dist/commands/booking/smoke.js +101 -0
- package/dist/commands/booking/smoke.js.map +1 -0
- package/dist/commands/cms/bind.d.ts +3 -0
- package/dist/commands/cms/bind.d.ts.map +1 -0
- package/dist/commands/cms/bind.js +212 -0
- package/dist/commands/cms/bind.js.map +1 -0
- package/dist/commands/cms/content.d.ts +40 -0
- package/dist/commands/cms/content.d.ts.map +1 -0
- package/dist/commands/cms/content.js +169 -0
- package/dist/commands/cms/content.js.map +1 -0
- package/dist/commands/cms/doctor.d.ts +3 -0
- package/dist/commands/cms/doctor.d.ts.map +1 -0
- package/dist/commands/cms/doctor.js +69 -0
- package/dist/commands/cms/doctor.js.map +1 -0
- package/dist/commands/cms/migrate.d.ts +3 -0
- package/dist/commands/cms/migrate.d.ts.map +1 -0
- package/dist/commands/cms/migrate.js +78 -0
- package/dist/commands/cms/migrate.js.map +1 -0
- package/dist/commands/cms/registry.d.ts +3 -0
- package/dist/commands/cms/registry.d.ts.map +1 -0
- package/dist/commands/cms/registry.js +161 -0
- package/dist/commands/cms/registry.js.map +1 -0
- package/dist/commands/cms/seed.d.ts +3 -0
- package/dist/commands/cms/seed.d.ts.map +1 -0
- package/dist/commands/cms/seed.js +102 -0
- package/dist/commands/cms/seed.js.map +1 -0
- package/dist/commands/cms/shared.d.ts +22 -0
- package/dist/commands/cms/shared.d.ts.map +1 -0
- package/dist/commands/cms/shared.js +82 -0
- package/dist/commands/cms/shared.js.map +1 -0
- package/dist/commands/doctor/target.d.ts +3 -0
- package/dist/commands/doctor/target.d.ts.map +1 -0
- package/dist/commands/doctor/target.js +46 -0
- package/dist/commands/doctor/target.js.map +1 -0
- package/dist/commands/env/list.d.ts +3 -0
- package/dist/commands/env/list.d.ts.map +1 -0
- package/dist/commands/env/list.js +28 -0
- package/dist/commands/env/list.js.map +1 -0
- package/dist/commands/env/set.d.ts +3 -0
- package/dist/commands/env/set.d.ts.map +1 -0
- package/dist/commands/env/set.js +36 -0
- package/dist/commands/env/set.js.map +1 -0
- package/dist/commands/env/use.d.ts +3 -0
- package/dist/commands/env/use.d.ts.map +1 -0
- package/dist/commands/env/use.js +15 -0
- package/dist/commands/env/use.js.map +1 -0
- package/dist/commands/legacy/connect.d.ts +3 -0
- package/dist/commands/legacy/connect.d.ts.map +1 -0
- package/dist/commands/legacy/connect.js +8 -0
- package/dist/commands/legacy/connect.js.map +1 -0
- package/dist/commands/legacy/pages.d.ts +3 -0
- package/dist/commands/legacy/pages.d.ts.map +1 -0
- package/dist/commands/legacy/pages.js +16 -0
- package/dist/commands/legacy/pages.js.map +1 -0
- package/dist/commands/legacy/spread.d.ts +3 -0
- package/dist/commands/legacy/spread.d.ts.map +1 -0
- package/dist/commands/legacy/spread.js +8 -0
- package/dist/commands/legacy/spread.js.map +1 -0
- package/dist/commands/legacy/sync.d.ts +3 -0
- package/dist/commands/legacy/sync.d.ts.map +1 -0
- package/dist/commands/legacy/sync.js +8 -0
- package/dist/commands/legacy/sync.js.map +1 -0
- package/dist/config/env-diff.d.ts +10 -0
- package/dist/config/env-diff.d.ts.map +1 -0
- package/dist/config/env-diff.js +24 -0
- package/dist/config/env-diff.js.map +1 -0
- package/dist/config/env-parser.d.ts +20 -0
- package/dist/config/env-parser.d.ts.map +1 -0
- package/dist/config/env-parser.js +70 -0
- package/dist/config/env-parser.js.map +1 -0
- package/dist/config/env-writer.d.ts +22 -0
- package/dist/config/env-writer.d.ts.map +1 -0
- package/dist/config/env-writer.js +172 -0
- package/dist/config/env-writer.js.map +1 -0
- package/dist/config/profile-store.d.ts +29 -0
- package/dist/config/profile-store.d.ts.map +1 -0
- package/dist/config/profile-store.js +257 -0
- package/dist/config/profile-store.js.map +1 -0
- package/dist/core/args.d.ts +11 -0
- package/dist/core/args.d.ts.map +1 -0
- package/dist/core/args.js +106 -0
- package/dist/core/args.js.map +1 -0
- package/dist/core/colors.d.ts +6 -0
- package/dist/core/colors.d.ts.map +1 -0
- package/dist/core/colors.js +29 -0
- package/dist/core/colors.js.map +1 -0
- package/dist/safety/target-guard.d.ts +16 -0
- package/dist/safety/target-guard.d.ts.map +1 -0
- package/dist/safety/target-guard.js +55 -0
- package/dist/safety/target-guard.js.map +1 -0
- package/dist/testing/booking-smoke.d.ts +17 -0
- package/dist/testing/booking-smoke.d.ts.map +1 -0
- package/dist/testing/booking-smoke.js +43 -0
- package/dist/testing/booking-smoke.js.map +1 -0
- package/dist/tests/agent-commands.test.d.ts +2 -0
- package/dist/tests/agent-commands.test.d.ts.map +1 -0
- package/dist/tests/agent-commands.test.js +180 -0
- package/dist/tests/agent-commands.test.js.map +1 -0
- package/dist/tests/agent-governance.test.d.ts +2 -0
- package/dist/tests/agent-governance.test.d.ts.map +1 -0
- package/dist/tests/agent-governance.test.js +233 -0
- package/dist/tests/agent-governance.test.js.map +1 -0
- package/dist/tests/app-commands.test.d.ts +2 -0
- package/dist/tests/app-commands.test.d.ts.map +1 -0
- package/dist/tests/app-commands.test.js +462 -0
- package/dist/tests/app-commands.test.js.map +1 -0
- package/dist/tests/booking-commands.test.d.ts +2 -0
- package/dist/tests/booking-commands.test.d.ts.map +1 -0
- package/dist/tests/booking-commands.test.js +204 -0
- package/dist/tests/booking-commands.test.js.map +1 -0
- package/dist/tests/booking-smoke.test.d.ts +2 -0
- package/dist/tests/booking-smoke.test.d.ts.map +1 -0
- package/dist/tests/booking-smoke.test.js +183 -0
- package/dist/tests/booking-smoke.test.js.map +1 -0
- package/dist/tests/cms-commands.test.d.ts +2 -0
- package/dist/tests/cms-commands.test.d.ts.map +1 -0
- package/dist/tests/cms-commands.test.js +254 -0
- package/dist/tests/cms-commands.test.js.map +1 -0
- package/dist/tests/cms-ops.test.d.ts +2 -0
- package/dist/tests/cms-ops.test.d.ts.map +1 -0
- package/dist/tests/cms-ops.test.js +125 -0
- package/dist/tests/cms-ops.test.js.map +1 -0
- package/dist/tests/env-writer.test.d.ts +2 -0
- package/dist/tests/env-writer.test.d.ts.map +1 -0
- package/dist/tests/env-writer.test.js +90 -0
- package/dist/tests/env-writer.test.js.map +1 -0
- package/dist/tests/profile-store.test.d.ts +2 -0
- package/dist/tests/profile-store.test.d.ts.map +1 -0
- package/dist/tests/profile-store.test.js +88 -0
- package/dist/tests/profile-store.test.js.map +1 -0
- package/dist/tests/target-guard.test.d.ts +2 -0
- package/dist/tests/target-guard.test.d.ts.map +1 -0
- package/dist/tests/target-guard.test.js +132 -0
- package/dist/tests/target-guard.test.js.map +1 -0
- package/dist/ui/logo.d.ts +2 -0
- package/dist/ui/logo.d.ts.map +1 -0
- package/dist/ui/logo.js +22 -0
- package/dist/ui/logo.js.map +1 -0
- package/package.json +17 -53
- package/.claude/settings.local.json +0 -36
- package/.cursor/rules.md +0 -203
- package/.eslintrc.js +0 -31
- package/CLAUDE.md +0 -100
- package/bin/cli.js +0 -116
- package/docs/ADDING_FRAMEWORK_DETECTORS.md +0 -391
- package/docs/ADDING_NEW_PROJECT_TYPE.md +0 -156
- package/docs/ARCHITECTURE_RELATIONSHIPS.md +0 -411
- package/docs/CLI_AUTHENTICATION.md +0 -214
- package/docs/CLI_PAGE_DETECTION_REQUIREMENTS.md +0 -519
- package/docs/CRM-PIPELINES-SEQUENCES-SPEC.md +0 -429
- package/docs/DETECTOR_ARCHITECTURE.md +0 -326
- package/docs/DEVELOPMENT.md +0 -194
- package/docs/IMPLEMENTATION_PHASES.md +0 -468
- package/docs/INTEGRATION_PATHS_ARCHITECTURE.md +0 -1543
- package/docs/OAUTH_CLARIFICATION.md +0 -258
- package/docs/OAUTH_SETUP_GUIDE_TEMPLATE.md +0 -211
- package/docs/PHASE_0_PROGRESS.md +0 -120
- package/docs/PHASE_1_COMPLETE.md +0 -366
- package/docs/PHASE_SUMMARY.md +0 -149
- package/docs/PLAN.md +0 -511
- package/docs/README.md +0 -56
- package/docs/STRIPE_INTEGRATION.md +0 -447
- package/docs/SUMMARY.md +0 -230
- package/docs/UPDATED_PLAN.md +0 -447
- package/docs/mcp_server/MCP_EXTENSION_GUIDE.md +0 -1313
- package/docs/mcp_server/MCP_SERVER_ARCHITECTURE.md +0 -1481
- package/docs/mcp_server/applicationOntology.ts +0 -817
- package/docs/mcp_server/cliApplications.ts +0 -639
- package/docs/mcp_server/crmOntology.ts +0 -1063
- package/docs/mcp_server/eventOntology.ts +0 -1183
- package/docs/mcp_server/formsOntology.ts +0 -1401
- package/docs/mcp_server/ontologySchemas.ts +0 -185
- package/docs/mcp_server/schema.ts +0 -250
- package/docs/microsass_production_machine/CLI_API_REFERENCE.md +0 -1197
- package/docs/microsass_production_machine/CLI_PRODUCT_VISION.md +0 -676
- package/docs/microsass_production_machine/CLI_REQUIREMENTS.md +0 -606
- package/docs/microsass_production_machine/CONNECTED_APPLICATIONS_SPEC.md +0 -390
- package/docs/microsass_production_machine/IMPLEMENTATION_ROADMAP.md +0 -725
- package/docs/microsass_production_machine/OBJECT_MAPPINGS.md +0 -808
- package/docs/microsass_production_machine/REFERENCE_IMPLEMENTATION.md +0 -532
- package/src/api/backend-client.js +0 -449
- package/src/commands/api-keys.js +0 -119
- package/src/commands/connect.js +0 -243
- package/src/commands/login.js +0 -332
- package/src/commands/logout.js +0 -30
- package/src/commands/mcp-server.js +0 -85
- package/src/commands/mcp-setup.js +0 -686
- package/src/commands/pages.js +0 -317
- package/src/commands/scaffold.js +0 -409
- package/src/commands/spread.js +0 -861
- package/src/commands/status.js +0 -62
- package/src/commands/sync.js +0 -169
- package/src/commands/upgrade.js +0 -48
- package/src/config/config-manager.js +0 -206
- package/src/detectors/api-client-detector.js +0 -85
- package/src/detectors/base-detector.js +0 -77
- package/src/detectors/database-detector.js +0 -245
- package/src/detectors/expo-detector.js +0 -166
- package/src/detectors/github-detector.js +0 -74
- package/src/detectors/index.js +0 -106
- package/src/detectors/mapping-suggestor.js +0 -119
- package/src/detectors/model-detector.js +0 -318
- package/src/detectors/nextjs-detector.js +0 -139
- package/src/detectors/oauth-detector.js +0 -122
- package/src/detectors/page-detector.js +0 -480
- package/src/detectors/registry.js +0 -121
- package/src/generators/api-client-generator.js +0 -223
- package/src/generators/api-only/client.js +0 -683
- package/src/generators/api-only/index.js +0 -96
- package/src/generators/api-only/types.js +0 -618
- package/src/generators/api-only/webhooks.js +0 -377
- package/src/generators/env-generator.js +0 -191
- package/src/generators/expo-auth-generator.js +0 -1009
- package/src/generators/gitignore-generator.js +0 -92
- package/src/generators/index.js +0 -166
- package/src/generators/manifest-generator.js +0 -154
- package/src/generators/mcp-guide-generator.js +0 -256
- package/src/generators/nextauth-generator.js +0 -247
- package/src/generators/oauth-guide-generator.js +0 -277
- package/src/generators/quickstart/components/index.js +0 -1699
- package/src/generators/quickstart/components-mobile/index.js +0 -1440
- package/src/generators/quickstart/database/convex.js +0 -1257
- package/src/generators/quickstart/database/index.js +0 -34
- package/src/generators/quickstart/database/supabase.js +0 -1132
- package/src/generators/quickstart/hooks/index.js +0 -1065
- package/src/generators/quickstart/index.js +0 -191
- package/src/generators/quickstart/pages/index.js +0 -1466
- package/src/generators/quickstart/screens/index.js +0 -1498
- package/src/logo.js +0 -116
- package/src/mcp/auth.js +0 -127
- package/src/mcp/registry/domains/applications.js +0 -516
- package/src/mcp/registry/domains/benefits.js +0 -798
- package/src/mcp/registry/domains/codegen.js +0 -894
- package/src/mcp/registry/domains/core.js +0 -324
- package/src/mcp/registry/domains/crm.js +0 -591
- package/src/mcp/registry/domains/events.js +0 -649
- package/src/mcp/registry/domains/forms.js +0 -696
- package/src/mcp/registry/index.js +0 -164
- package/src/mcp/server.js +0 -116
- package/src/utils/file-utils.js +0 -117
- package/src/utils/init-helpers.js +0 -243
- package/src/utils/prompt-utils.js +0 -195
- package/templates/CLAUDE.md +0 -86
- package/tests/api-client-detector.test.js +0 -214
- package/tests/api-client-generator.test.js +0 -176
- package/tests/backend-client.test.js +0 -640
- package/tests/base-detector.test.js +0 -101
- package/tests/commands/login.test.js +0 -143
- package/tests/commands/logout.test.js +0 -84
- package/tests/commands/status.test.js +0 -167
- package/tests/config-manager.test.js +0 -321
- package/tests/database-detector.test.js +0 -221
- package/tests/detector-index.test.js +0 -209
- package/tests/detector-registry.test.js +0 -93
- package/tests/env-generator.test.js +0 -278
- package/tests/expo-detector.test.js +0 -263
- package/tests/file-utils.test.js +0 -194
- package/tests/generators-index.test.js +0 -454
- package/tests/github-detector.test.js +0 -145
- package/tests/gitignore-generator.test.js +0 -109
- package/tests/logo.test.js +0 -96
- package/tests/nextauth-generator.test.js +0 -255
- package/tests/nextjs-detector.test.js +0 -235
- package/tests/oauth-detector.test.js +0 -264
- package/tests/oauth-guide-generator.test.js +0 -273
- package/tests/page-detector.test.js +0 -371
package/src/commands/spread.js
DELETED
|
@@ -1,861 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spread Command
|
|
3
|
-
* Main command for setting up boilerplate integration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const configManager = require('../config/config-manager');
|
|
9
|
-
const backendClient = require('../api/backend-client');
|
|
10
|
-
const projectDetector = require('../detectors');
|
|
11
|
-
const fileGenerator = require('../generators');
|
|
12
|
-
const manifestGenerator = require('../generators/manifest-generator');
|
|
13
|
-
const pageDetector = require('../detectors/page-detector');
|
|
14
|
-
const { suggestMappings } = require('../detectors/mapping-suggestor');
|
|
15
|
-
const { generateProjectPathHash } = require('../utils/file-utils');
|
|
16
|
-
const {
|
|
17
|
-
createOrganization,
|
|
18
|
-
generateNewApiKey,
|
|
19
|
-
checkGitStatusBeforeGeneration,
|
|
20
|
-
requireAuth,
|
|
21
|
-
} = require('../utils/init-helpers');
|
|
22
|
-
const inquirer = require('inquirer');
|
|
23
|
-
const chalk = require('chalk');
|
|
24
|
-
const pkg = require('../../package.json');
|
|
25
|
-
const { showMainMenu, executeMenuAction } = require('../utils/prompt-utils');
|
|
26
|
-
|
|
27
|
-
async function handleSpread() {
|
|
28
|
-
requireAuth(configManager);
|
|
29
|
-
|
|
30
|
-
console.log(chalk.cyan(' 🍰 Setting up your Layer Cake integration...\n'));
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Step 1: Detect project
|
|
34
|
-
console.log(chalk.gray(' 🔍 Detecting project...\n'));
|
|
35
|
-
const detection = projectDetector.detect();
|
|
36
|
-
|
|
37
|
-
// Display framework detection results
|
|
38
|
-
if (detection.framework.type) {
|
|
39
|
-
const frameworkNames = {
|
|
40
|
-
'nextjs': 'Next.js',
|
|
41
|
-
'expo': 'Expo',
|
|
42
|
-
'react-native': 'React Native',
|
|
43
|
-
};
|
|
44
|
-
const frameworkName = frameworkNames[detection.framework.type] || detection.framework.type;
|
|
45
|
-
console.log(chalk.green(` ✅ Detected ${frameworkName} project`));
|
|
46
|
-
|
|
47
|
-
const meta = detection.framework.metadata || {};
|
|
48
|
-
|
|
49
|
-
if (detection.framework.type === 'nextjs') {
|
|
50
|
-
if (meta.version) {
|
|
51
|
-
console.log(chalk.gray(` Version: ${meta.version}`));
|
|
52
|
-
}
|
|
53
|
-
if (meta.routerType) {
|
|
54
|
-
console.log(chalk.gray(` Router: ${meta.routerType === 'app' ? 'App Router' : 'Pages Router'}`));
|
|
55
|
-
}
|
|
56
|
-
if (meta.hasTypeScript) {
|
|
57
|
-
console.log(chalk.gray(' TypeScript: Yes'));
|
|
58
|
-
}
|
|
59
|
-
} else if (detection.framework.type === 'expo' || detection.framework.type === 'react-native') {
|
|
60
|
-
if (meta.expoVersion) {
|
|
61
|
-
console.log(chalk.gray(` Expo SDK: ${meta.expoVersion}`));
|
|
62
|
-
}
|
|
63
|
-
if (meta.reactNativeVersion) {
|
|
64
|
-
console.log(chalk.gray(` React Native: ${meta.reactNativeVersion}`));
|
|
65
|
-
}
|
|
66
|
-
if (meta.routerType) {
|
|
67
|
-
const routerName = meta.routerType === 'expo-router' ? 'Expo Router' :
|
|
68
|
-
meta.routerType === 'react-navigation' ? 'React Navigation' : 'Native';
|
|
69
|
-
console.log(chalk.gray(` Navigation: ${routerName}`));
|
|
70
|
-
}
|
|
71
|
-
if (meta.hasTypeScript) {
|
|
72
|
-
console.log(chalk.gray(' TypeScript: Yes'));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Show supported features
|
|
77
|
-
const features = detection.framework.supportedFeatures;
|
|
78
|
-
const supportedFeatures = Object.entries(features)
|
|
79
|
-
.filter(([_, supported]) => supported === true || supported === 'manual')
|
|
80
|
-
.map(([name]) => name);
|
|
81
|
-
|
|
82
|
-
if (supportedFeatures.length > 0) {
|
|
83
|
-
console.log(chalk.gray(` Supported features: ${supportedFeatures.join(', ')}`));
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
console.log(chalk.yellow(' ⚠️ Could not detect project type'));
|
|
87
|
-
const { continueAnyway } = await inquirer.prompt([
|
|
88
|
-
{
|
|
89
|
-
type: 'confirm',
|
|
90
|
-
name: 'continueAnyway',
|
|
91
|
-
message: 'Continue with basic setup anyway?',
|
|
92
|
-
default: false,
|
|
93
|
-
},
|
|
94
|
-
]);
|
|
95
|
-
|
|
96
|
-
if (!continueAnyway) {
|
|
97
|
-
console.log(chalk.gray('\n Setup cancelled.\n'));
|
|
98
|
-
const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
|
|
99
|
-
await executeMenuAction(action);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Display GitHub detection
|
|
105
|
-
if (detection.github.isGitHub) {
|
|
106
|
-
console.log(chalk.green(` ✅ Detected GitHub repository: ${detection.github.owner}/${detection.github.repo}`));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Display API client detection
|
|
110
|
-
if (detection.apiClient.hasApiClient) {
|
|
111
|
-
console.log(chalk.yellow(` ⚠️ Existing API client found: ${detection.apiClient.clientPath}`));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Display OAuth detection
|
|
115
|
-
if (detection.oauth.hasOAuth) {
|
|
116
|
-
console.log(chalk.yellow(` ⚠️ Existing OAuth setup detected: ${detection.oauth.oauthType}`));
|
|
117
|
-
if (detection.oauth.providers.length > 0) {
|
|
118
|
-
console.log(chalk.gray(` Providers: ${detection.oauth.providers.join(', ')}`));
|
|
119
|
-
}
|
|
120
|
-
if (detection.oauth.configPath) {
|
|
121
|
-
console.log(chalk.gray(` Config: ${detection.oauth.configPath}`));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Display model detection
|
|
126
|
-
if (detection.models && detection.models.hasModels) {
|
|
127
|
-
console.log(chalk.green(` ✅ Detected ${detection.models.models.length} model(s)`));
|
|
128
|
-
for (const model of detection.models.models.slice(0, 5)) {
|
|
129
|
-
console.log(chalk.gray(` • ${model.name} (${model.source}) [${model.fields.length} fields]`));
|
|
130
|
-
}
|
|
131
|
-
if (detection.models.models.length > 5) {
|
|
132
|
-
console.log(chalk.gray(` ... and ${detection.models.models.length - 5} more`));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Scan routes with HTTP methods
|
|
137
|
-
const detectedRoutes = pageDetector.detect(
|
|
138
|
-
detection.projectPath,
|
|
139
|
-
detection.framework.type,
|
|
140
|
-
detection.framework.metadata || {}
|
|
141
|
-
);
|
|
142
|
-
const apiRoutes = detectedRoutes.filter(r => r.pageType === 'api_route');
|
|
143
|
-
if (apiRoutes.length > 0) {
|
|
144
|
-
console.log(chalk.green(` ✅ Detected ${apiRoutes.length} API route(s)`));
|
|
145
|
-
for (const route of apiRoutes.slice(0, 5)) {
|
|
146
|
-
const methods = route.methods ? route.methods.join(', ') : 'GET, POST';
|
|
147
|
-
console.log(chalk.gray(` • ${route.path} [${methods}]`));
|
|
148
|
-
}
|
|
149
|
-
if (apiRoutes.length > 5) {
|
|
150
|
-
console.log(chalk.gray(` ... and ${apiRoutes.length - 5} more`));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Compute suggested mappings
|
|
155
|
-
const models = detection.models ? detection.models.models : [];
|
|
156
|
-
const mappings = suggestMappings(models);
|
|
157
|
-
if (mappings.length > 0) {
|
|
158
|
-
console.log(chalk.green(` ✅ ${mappings.length} suggested mapping(s)`));
|
|
159
|
-
for (const mapping of mappings) {
|
|
160
|
-
console.log(chalk.gray(` • ${mapping.localModel} → ${mapping.platformType} (${mapping.confidence}% confidence)`));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
console.log('');
|
|
165
|
-
|
|
166
|
-
// Step 1.5: Project name
|
|
167
|
-
const folderName = path.basename(detection.projectPath);
|
|
168
|
-
const defaultProjectName = detection.github.repo || folderName;
|
|
169
|
-
|
|
170
|
-
const { projectName } = await inquirer.prompt([
|
|
171
|
-
{
|
|
172
|
-
type: 'input',
|
|
173
|
-
name: 'projectName',
|
|
174
|
-
message: 'Project name:',
|
|
175
|
-
default: defaultProjectName,
|
|
176
|
-
validate: (input) => input.trim().length > 0 || 'Project name is required',
|
|
177
|
-
},
|
|
178
|
-
]);
|
|
179
|
-
console.log(chalk.green(` ✅ Project: ${projectName}\n`));
|
|
180
|
-
|
|
181
|
-
// Step 2: Organization selection
|
|
182
|
-
console.log(chalk.cyan(' 📦 Organization Setup\n'));
|
|
183
|
-
let organizationId;
|
|
184
|
-
let organizationName;
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
const orgsResponse = await backendClient.getOrganizations();
|
|
188
|
-
// Handle different response formats
|
|
189
|
-
const organizations = Array.isArray(orgsResponse)
|
|
190
|
-
? orgsResponse
|
|
191
|
-
: orgsResponse.organizations || orgsResponse.data || [];
|
|
192
|
-
|
|
193
|
-
if (organizations.length === 0) {
|
|
194
|
-
// No organizations, create one
|
|
195
|
-
const { orgName } = await inquirer.prompt([
|
|
196
|
-
{
|
|
197
|
-
type: 'input',
|
|
198
|
-
name: 'orgName',
|
|
199
|
-
message: 'Organization name:',
|
|
200
|
-
default: detection.github.repo || 'My Organization',
|
|
201
|
-
validate: (input) => input.trim().length > 0 || 'Organization name is required',
|
|
202
|
-
},
|
|
203
|
-
]);
|
|
204
|
-
|
|
205
|
-
const result = await createOrganization(orgName);
|
|
206
|
-
organizationId = result.organizationId;
|
|
207
|
-
organizationName = result.organizationName;
|
|
208
|
-
} else {
|
|
209
|
-
// Select or create organization
|
|
210
|
-
const { orgChoice } = await inquirer.prompt([
|
|
211
|
-
{
|
|
212
|
-
type: 'list',
|
|
213
|
-
name: 'orgChoice',
|
|
214
|
-
message: 'Select organization:',
|
|
215
|
-
choices: [
|
|
216
|
-
...organizations.map(org => ({
|
|
217
|
-
name: `${org.name} (${org.id})`,
|
|
218
|
-
value: org.id,
|
|
219
|
-
})),
|
|
220
|
-
{ name: '➕ Create new organization', value: '__create__' },
|
|
221
|
-
],
|
|
222
|
-
},
|
|
223
|
-
]);
|
|
224
|
-
|
|
225
|
-
if (orgChoice === '__create__') {
|
|
226
|
-
const { orgName } = await inquirer.prompt([
|
|
227
|
-
{
|
|
228
|
-
type: 'input',
|
|
229
|
-
name: 'orgName',
|
|
230
|
-
message: 'Organization name:',
|
|
231
|
-
default: detection.github.repo || 'My Organization',
|
|
232
|
-
validate: (input) => input.trim().length > 0 || 'Organization name is required',
|
|
233
|
-
},
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
const result = await createOrganization(orgName);
|
|
237
|
-
organizationId = result.organizationId;
|
|
238
|
-
organizationName = result.organizationName;
|
|
239
|
-
} else {
|
|
240
|
-
const selectedOrg = organizations.find(org => org.id === orgChoice);
|
|
241
|
-
organizationId = orgChoice;
|
|
242
|
-
organizationName = selectedOrg.name;
|
|
243
|
-
console.log(chalk.green(` ✅ Selected organization: ${organizationName}\n`));
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
} catch (error) {
|
|
247
|
-
console.error(chalk.red(` ❌ Error managing organizations: ${error.message}\n`));
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Step 3: API Key Setup
|
|
252
|
-
console.log(chalk.cyan(' 🔑 API Key Setup\n'));
|
|
253
|
-
let apiKey;
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
// First, check if .env.local already has an API key configured
|
|
257
|
-
const envPath = path.join(detection.projectPath, '.env.local');
|
|
258
|
-
let existingEnvKey = null;
|
|
259
|
-
if (fs.existsSync(envPath)) {
|
|
260
|
-
try {
|
|
261
|
-
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
262
|
-
const match = envContent.match(/^L4YERCAK3_API_KEY=(.+)$/m);
|
|
263
|
-
if (match && match[1] && match[1].startsWith('sk_')) {
|
|
264
|
-
existingEnvKey = match[1].trim();
|
|
265
|
-
}
|
|
266
|
-
} catch {
|
|
267
|
-
// Ignore read errors
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (existingEnvKey) {
|
|
272
|
-
// Found existing API key in .env.local
|
|
273
|
-
const keyPreview = `${existingEnvKey.substring(0, 12)}...`;
|
|
274
|
-
console.log(chalk.green(` ✅ Found existing API key in .env.local (${keyPreview})\n`));
|
|
275
|
-
|
|
276
|
-
const { useExisting } = await inquirer.prompt([
|
|
277
|
-
{
|
|
278
|
-
type: 'list',
|
|
279
|
-
name: 'useExisting',
|
|
280
|
-
message: 'What would you like to do?',
|
|
281
|
-
choices: [
|
|
282
|
-
{ name: 'Keep existing API key (recommended)', value: 'keep' },
|
|
283
|
-
{ name: 'Generate a new API key', value: 'generate' },
|
|
284
|
-
],
|
|
285
|
-
},
|
|
286
|
-
]);
|
|
287
|
-
|
|
288
|
-
if (useExisting === 'keep') {
|
|
289
|
-
apiKey = null; // Will preserve existing key in .env.local
|
|
290
|
-
console.log(chalk.green(` ✅ Keeping existing API key\n`));
|
|
291
|
-
} else {
|
|
292
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
// No existing key in .env.local - check backend for keys
|
|
296
|
-
console.log(chalk.gray(' Checking existing API keys...'));
|
|
297
|
-
let existingKeys = null;
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
existingKeys = await backendClient.listApiKeys(organizationId);
|
|
301
|
-
} catch (listError) {
|
|
302
|
-
// If listing fails, continue to try generating
|
|
303
|
-
console.log(chalk.gray(' Could not check existing keys, attempting to generate...'));
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (existingKeys && existingKeys.canCreateMore === false) {
|
|
307
|
-
// At API key limit - inform user and exit (only if explicitly false, not undefined)
|
|
308
|
-
console.log(chalk.yellow(` ⚠️ You've reached your API key limit (${existingKeys.keys?.length || 0} key(s))`));
|
|
309
|
-
if (existingKeys.limitDescription) {
|
|
310
|
-
console.log(chalk.gray(` ${existingKeys.limitDescription}`));
|
|
311
|
-
}
|
|
312
|
-
console.log(chalk.cyan('\n To continue, either:'));
|
|
313
|
-
console.log(chalk.gray(' • Delete an existing key at https://app.l4yercak3.com?openWindow=integrations&panel=api-keys'));
|
|
314
|
-
console.log(chalk.gray(' • Upgrade your plan at https://app.l4yercak3.com?openWindow=store\n'));
|
|
315
|
-
const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
|
|
316
|
-
await executeMenuAction(action);
|
|
317
|
-
return;
|
|
318
|
-
} else if (existingKeys && existingKeys.keys && existingKeys.keys.length > 0) {
|
|
319
|
-
// Has existing keys on backend - offer to reuse or generate new
|
|
320
|
-
const activeKeys = existingKeys.keys.filter(k => k.status === 'active');
|
|
321
|
-
|
|
322
|
-
if (activeKeys.length > 0) {
|
|
323
|
-
console.log(chalk.gray(` Found ${activeKeys.length} active API key(s)\n`));
|
|
324
|
-
|
|
325
|
-
const keyChoices = activeKeys.map(key => ({
|
|
326
|
-
name: `Use ${key.name} (${key.keyPreview})`,
|
|
327
|
-
value: key.id,
|
|
328
|
-
}));
|
|
329
|
-
keyChoices.push({ name: '➕ Generate a new API key', value: '__generate__' });
|
|
330
|
-
|
|
331
|
-
const { keyChoice } = await inquirer.prompt([
|
|
332
|
-
{
|
|
333
|
-
type: 'list',
|
|
334
|
-
name: 'keyChoice',
|
|
335
|
-
message: 'Which API key would you like to use?',
|
|
336
|
-
choices: keyChoices,
|
|
337
|
-
},
|
|
338
|
-
]);
|
|
339
|
-
|
|
340
|
-
if (keyChoice === '__generate__') {
|
|
341
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
342
|
-
} else {
|
|
343
|
-
// User selected existing key - prompt for the full key
|
|
344
|
-
const selectedKey = activeKeys.find(k => k.id === keyChoice);
|
|
345
|
-
console.log(chalk.yellow(`\n ⚠️ For security, we can't retrieve the full API key.`));
|
|
346
|
-
console.log(chalk.gray(` You selected: ${selectedKey.name} (${selectedKey.keyPreview})`));
|
|
347
|
-
console.log(chalk.gray(` Enter the key if you have it, or press Enter to generate a new one.\n`));
|
|
348
|
-
|
|
349
|
-
const { existingKey } = await inquirer.prompt([
|
|
350
|
-
{
|
|
351
|
-
type: 'input',
|
|
352
|
-
name: 'existingKey',
|
|
353
|
-
message: 'Enter your API key (or press Enter to generate new):',
|
|
354
|
-
},
|
|
355
|
-
]);
|
|
356
|
-
|
|
357
|
-
if (existingKey.trim()) {
|
|
358
|
-
apiKey = existingKey.trim();
|
|
359
|
-
console.log(chalk.green(` ✅ Using existing API key\n`));
|
|
360
|
-
} else {
|
|
361
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
} else {
|
|
365
|
-
// Only revoked keys exist - generate new
|
|
366
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
367
|
-
}
|
|
368
|
-
} else {
|
|
369
|
-
// No existing keys - generate one
|
|
370
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
} catch (error) {
|
|
374
|
-
// Handle specific error codes
|
|
375
|
-
if (error.code === 'API_KEY_LIMIT_REACHED') {
|
|
376
|
-
console.log(chalk.yellow(`\n ⚠️ ${error.message}`));
|
|
377
|
-
if (error.suggestion) {
|
|
378
|
-
console.log(chalk.gray(` ${error.suggestion}`));
|
|
379
|
-
}
|
|
380
|
-
console.log(chalk.cyan('\n To continue, either:'));
|
|
381
|
-
console.log(chalk.gray(' • Delete an existing key at https://app.l4yercak3.com?openWindow=integrations&panel=api-keys'));
|
|
382
|
-
console.log(chalk.gray(' • Upgrade your plan at https://app.l4yercak3.com?openWindow=store\n'));
|
|
383
|
-
const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
|
|
384
|
-
await executeMenuAction(action);
|
|
385
|
-
return;
|
|
386
|
-
} else if (error.code === 'API_KEY_ALREADY_LINKED') {
|
|
387
|
-
console.log(chalk.yellow(`\n ⚠️ ${error.message}`));
|
|
388
|
-
if (error.suggestion) {
|
|
389
|
-
console.log(chalk.gray(` ${error.suggestion}`));
|
|
390
|
-
}
|
|
391
|
-
console.log(chalk.cyan('\n Each API key can only be connected to one application.'));
|
|
392
|
-
console.log(chalk.gray(' Generate a new API key for this project.\n'));
|
|
393
|
-
|
|
394
|
-
// Offer to generate a new key
|
|
395
|
-
const { generateNew } = await inquirer.prompt([
|
|
396
|
-
{
|
|
397
|
-
type: 'confirm',
|
|
398
|
-
name: 'generateNew',
|
|
399
|
-
message: 'Generate a new API key for this project?',
|
|
400
|
-
default: true,
|
|
401
|
-
},
|
|
402
|
-
]);
|
|
403
|
-
|
|
404
|
-
if (generateNew) {
|
|
405
|
-
apiKey = await generateNewApiKey(organizationId);
|
|
406
|
-
} else {
|
|
407
|
-
const action = await showMainMenu({ isLoggedIn: true, isInProject: true });
|
|
408
|
-
await executeMenuAction(action);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
} else if (error.code === 'SESSION_EXPIRED' || error.code === 'INVALID_SESSION') {
|
|
412
|
-
console.log(chalk.red(`\n ❌ Session expired. Please run "l4yercak3 login" again.\n`));
|
|
413
|
-
process.exit(1);
|
|
414
|
-
} else if (error.code === 'NOT_AUTHORIZED' || error.code === 'UNAUTHORIZED') {
|
|
415
|
-
console.log(chalk.red(`\n ❌ You don't have permission to manage API keys for this organization.\n`));
|
|
416
|
-
process.exit(1);
|
|
417
|
-
} else {
|
|
418
|
-
console.error(chalk.red(` ❌ Error setting up API key: ${error.message}\n`));
|
|
419
|
-
process.exit(1);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Step 4: Feature selection
|
|
424
|
-
// Auto-check features based on detected model mappings
|
|
425
|
-
const platformTypeToFeature = {
|
|
426
|
-
contact: 'crm',
|
|
427
|
-
booking: 'events',
|
|
428
|
-
event: 'events',
|
|
429
|
-
product: 'products',
|
|
430
|
-
invoice: 'invoicing',
|
|
431
|
-
project: 'projects',
|
|
432
|
-
form: 'forms',
|
|
433
|
-
certificate: 'certificates',
|
|
434
|
-
benefit: 'benefits',
|
|
435
|
-
};
|
|
436
|
-
const autoCheckedFeatures = new Set(['crm']); // CRM always default
|
|
437
|
-
for (const mapping of mappings) {
|
|
438
|
-
const feature = platformTypeToFeature[mapping.platformType];
|
|
439
|
-
if (feature) autoCheckedFeatures.add(feature);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
console.log(chalk.cyan(' ⚙️ Feature Selection\n'));
|
|
443
|
-
if (mappings.length > 0) {
|
|
444
|
-
console.log(chalk.gray(' Features pre-selected based on detected models:\n'));
|
|
445
|
-
}
|
|
446
|
-
const { features } = await inquirer.prompt([
|
|
447
|
-
{
|
|
448
|
-
type: 'checkbox',
|
|
449
|
-
name: 'features',
|
|
450
|
-
message: 'Select features to enable:',
|
|
451
|
-
choices: [
|
|
452
|
-
{ name: 'CRM (contacts, organizations)', value: 'crm', checked: autoCheckedFeatures.has('crm') },
|
|
453
|
-
{ name: 'Events (event management, registrations)', value: 'events', checked: autoCheckedFeatures.has('events') },
|
|
454
|
-
{ name: 'Forms (form builder, submissions)', value: 'forms', checked: autoCheckedFeatures.has('forms') },
|
|
455
|
-
{ name: 'Products (product catalog, inventory)', value: 'products', checked: autoCheckedFeatures.has('products') },
|
|
456
|
-
{ name: 'Checkout (cart, payments)', value: 'checkout', checked: autoCheckedFeatures.has('checkout') },
|
|
457
|
-
{ name: 'Invoicing (B2B/B2C invoices)', value: 'invoicing', checked: autoCheckedFeatures.has('invoicing') },
|
|
458
|
-
{ name: 'Benefits (claims, commissions)', value: 'benefits', checked: autoCheckedFeatures.has('benefits') },
|
|
459
|
-
{ name: 'Certificates (CME, attendance)', value: 'certificates', checked: autoCheckedFeatures.has('certificates') },
|
|
460
|
-
{ name: 'Projects (task management)', value: 'projects', checked: autoCheckedFeatures.has('projects') },
|
|
461
|
-
{ name: 'OAuth Authentication', value: 'oauth', checked: false },
|
|
462
|
-
],
|
|
463
|
-
},
|
|
464
|
-
]);
|
|
465
|
-
|
|
466
|
-
// Step 4.5: Integration path selection
|
|
467
|
-
console.log(chalk.cyan('\n 🛤️ Integration Path\n'));
|
|
468
|
-
const { integrationPath } = await inquirer.prompt([
|
|
469
|
-
{
|
|
470
|
-
type: 'list',
|
|
471
|
-
name: 'integrationPath',
|
|
472
|
-
message: 'Choose your integration approach:',
|
|
473
|
-
choices: [
|
|
474
|
-
{
|
|
475
|
-
name: 'Quick Start (Recommended) - Full-stack with UI components & database',
|
|
476
|
-
value: 'quickstart',
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
name: 'API Only - Just the typed API client, you build the UI',
|
|
480
|
-
value: 'api-only',
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
name: 'MCP-Assisted - AI-powered custom generation with Claude Code',
|
|
484
|
-
value: 'mcp-assisted',
|
|
485
|
-
},
|
|
486
|
-
],
|
|
487
|
-
},
|
|
488
|
-
]);
|
|
489
|
-
console.log(chalk.green(` ✅ Path: ${integrationPath === 'quickstart' ? 'Quick Start' : integrationPath === 'api-only' ? 'API Only' : 'MCP-Assisted'}\n`));
|
|
490
|
-
|
|
491
|
-
// Step 4.6: Database selection (for Quick Start path when no DB detected)
|
|
492
|
-
let selectedDatabase = null;
|
|
493
|
-
if (integrationPath === 'quickstart') {
|
|
494
|
-
const dbDetection = detection.database || { hasDatabase: false };
|
|
495
|
-
|
|
496
|
-
if (!dbDetection.hasDatabase) {
|
|
497
|
-
console.log(chalk.yellow(' ℹ️ No database detected in your project.\n'));
|
|
498
|
-
|
|
499
|
-
const { database } = await inquirer.prompt([
|
|
500
|
-
{
|
|
501
|
-
type: 'list',
|
|
502
|
-
name: 'database',
|
|
503
|
-
message: 'Which database would you like to use?',
|
|
504
|
-
choices: [
|
|
505
|
-
{
|
|
506
|
-
name: 'Convex (Recommended) - Real-time, serverless, TypeScript-first',
|
|
507
|
-
value: 'convex',
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
name: 'Supabase - PostgreSQL with Auth, Storage, and Edge Functions',
|
|
511
|
-
value: 'supabase',
|
|
512
|
-
},
|
|
513
|
-
{
|
|
514
|
-
name: 'None - I\'ll set up my own database later',
|
|
515
|
-
value: 'none',
|
|
516
|
-
},
|
|
517
|
-
],
|
|
518
|
-
},
|
|
519
|
-
]);
|
|
520
|
-
|
|
521
|
-
selectedDatabase = database !== 'none' ? database : null;
|
|
522
|
-
if (selectedDatabase) {
|
|
523
|
-
console.log(chalk.green(` ✅ Database: ${selectedDatabase}\n`));
|
|
524
|
-
}
|
|
525
|
-
} else {
|
|
526
|
-
console.log(chalk.green(` ✅ Detected ${dbDetection.primary?.type || 'existing'} database\n`));
|
|
527
|
-
selectedDatabase = dbDetection.primary?.type || 'existing';
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Step 5: OAuth provider selection (if OAuth enabled)
|
|
532
|
-
let oauthProviders = [];
|
|
533
|
-
if (features.includes('oauth')) {
|
|
534
|
-
const { providers } = await inquirer.prompt([
|
|
535
|
-
{
|
|
536
|
-
type: 'checkbox',
|
|
537
|
-
name: 'providers',
|
|
538
|
-
message: 'Select OAuth providers:',
|
|
539
|
-
choices: [
|
|
540
|
-
{ name: 'Google', value: 'google', checked: true },
|
|
541
|
-
{ name: 'Microsoft', value: 'microsoft', checked: true },
|
|
542
|
-
{ name: 'GitHub', value: 'github', checked: false },
|
|
543
|
-
],
|
|
544
|
-
},
|
|
545
|
-
]);
|
|
546
|
-
oauthProviders = providers;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Step 6: Backend URL (fixed to Convex HTTP endpoint)
|
|
550
|
-
const backendUrl = 'https://agreeable-lion-828.convex.site';
|
|
551
|
-
|
|
552
|
-
// Step 7: Production domain (for OAuth redirect URIs)
|
|
553
|
-
let productionDomain = null;
|
|
554
|
-
if (features.includes('oauth')) {
|
|
555
|
-
console.log(chalk.gray('\n ℹ️ The following settings are written to .env.local for local development.'));
|
|
556
|
-
console.log(chalk.gray(' For production, set NEXTAUTH_URL in your hosting platform (e.g., Vercel).\n'));
|
|
557
|
-
|
|
558
|
-
const { configureNow } = await inquirer.prompt([
|
|
559
|
-
{
|
|
560
|
-
type: 'confirm',
|
|
561
|
-
name: 'configureNow',
|
|
562
|
-
message: 'Configure production domain now? (You can skip and do this later)',
|
|
563
|
-
default: true,
|
|
564
|
-
},
|
|
565
|
-
]);
|
|
566
|
-
|
|
567
|
-
if (configureNow) {
|
|
568
|
-
const { domain } = await inquirer.prompt([
|
|
569
|
-
{
|
|
570
|
-
type: 'input',
|
|
571
|
-
name: 'domain',
|
|
572
|
-
message: 'Production domain (for OAuth redirect URIs):',
|
|
573
|
-
default: detection.github.repo ? `${detection.github.repo}.vercel.app` : 'your-domain.com',
|
|
574
|
-
},
|
|
575
|
-
]);
|
|
576
|
-
productionDomain = domain;
|
|
577
|
-
} else {
|
|
578
|
-
console.log(chalk.gray(' Skipping production domain configuration.'));
|
|
579
|
-
console.log(chalk.gray(' Set NEXTAUTH_URL in your hosting platform when deploying.\n'));
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// Step 8: Check for uncommitted changes before generating files
|
|
584
|
-
const shouldProceed = await checkGitStatusBeforeGeneration(detection.projectPath);
|
|
585
|
-
if (!shouldProceed) {
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Step 9: Generate files
|
|
590
|
-
console.log(chalk.cyan('\n 📝 Generating files...\n'));
|
|
591
|
-
|
|
592
|
-
// Extract framework metadata for generation
|
|
593
|
-
const frameworkMeta = detection.framework.metadata || {};
|
|
594
|
-
const isTypeScript = frameworkMeta.hasTypeScript || false;
|
|
595
|
-
const routerType = frameworkMeta.routerType || 'pages';
|
|
596
|
-
|
|
597
|
-
const generationOptions = {
|
|
598
|
-
projectPath: detection.projectPath,
|
|
599
|
-
apiKey,
|
|
600
|
-
backendUrl,
|
|
601
|
-
organizationId,
|
|
602
|
-
organizationName,
|
|
603
|
-
features,
|
|
604
|
-
oauthProviders,
|
|
605
|
-
productionDomain,
|
|
606
|
-
appName: projectName,
|
|
607
|
-
isTypeScript,
|
|
608
|
-
routerType,
|
|
609
|
-
frameworkType: detection.framework.type || 'unknown',
|
|
610
|
-
integrationPath,
|
|
611
|
-
selectedDatabase,
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
// Debug: Log generation options
|
|
615
|
-
if (process.env.L4YERCAK3_DEBUG) {
|
|
616
|
-
console.log('\n[DEBUG] Spread: Generation options:');
|
|
617
|
-
console.log(` frameworkType: "${generationOptions.frameworkType}"`);
|
|
618
|
-
console.log(` integrationPath: "${generationOptions.integrationPath}"`);
|
|
619
|
-
console.log(` isTypeScript: ${generationOptions.isTypeScript}`);
|
|
620
|
-
console.log(` features: [${generationOptions.features.join(', ')}]`);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
const generatedFiles = await fileGenerator.generate(generationOptions);
|
|
624
|
-
|
|
625
|
-
// Display results
|
|
626
|
-
console.log(chalk.green(' ✅ Files generated:\n'));
|
|
627
|
-
|
|
628
|
-
// API client files (api-only and quickstart paths)
|
|
629
|
-
if (generatedFiles.apiClient) {
|
|
630
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.apiClient)}`));
|
|
631
|
-
}
|
|
632
|
-
if (generatedFiles.types) {
|
|
633
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.types)}`));
|
|
634
|
-
}
|
|
635
|
-
if (generatedFiles.webhooks) {
|
|
636
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.webhooks)}`));
|
|
637
|
-
}
|
|
638
|
-
if (generatedFiles.index) {
|
|
639
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.index)}`));
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// MCP files (mcp-assisted path)
|
|
643
|
-
if (generatedFiles.mcpConfig) {
|
|
644
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.mcpConfig)}`));
|
|
645
|
-
}
|
|
646
|
-
if (generatedFiles.mcpGuide) {
|
|
647
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.mcpGuide)}`));
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// Common files
|
|
651
|
-
if (generatedFiles.envFile) {
|
|
652
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.envFile)}`));
|
|
653
|
-
}
|
|
654
|
-
if (generatedFiles.nextauth) {
|
|
655
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.nextauth)}`));
|
|
656
|
-
}
|
|
657
|
-
if (generatedFiles.oauthGuide) {
|
|
658
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.oauthGuide)}`));
|
|
659
|
-
}
|
|
660
|
-
if (generatedFiles.gitignore) {
|
|
661
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), generatedFiles.gitignore)} (updated)`));
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Generate .l4yercak3.json manifest
|
|
665
|
-
const manifestPath = manifestGenerator.generate({
|
|
666
|
-
projectPath: detection.projectPath,
|
|
667
|
-
detection,
|
|
668
|
-
models,
|
|
669
|
-
routes: detectedRoutes,
|
|
670
|
-
mappings,
|
|
671
|
-
});
|
|
672
|
-
console.log(chalk.gray(` • ${path.relative(process.cwd(), manifestPath)}`));
|
|
673
|
-
|
|
674
|
-
// Step 9: Register application with backend
|
|
675
|
-
console.log(chalk.cyan('\n 🔗 Registering with L4YERCAK3...\n'));
|
|
676
|
-
|
|
677
|
-
const projectPathHash = generateProjectPathHash(detection.projectPath);
|
|
678
|
-
let applicationId = null;
|
|
679
|
-
let isUpdate = false;
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
// Check if application already exists for this project
|
|
683
|
-
const existingApp = await backendClient.checkExistingApplication(organizationId, projectPathHash);
|
|
684
|
-
|
|
685
|
-
// Helper to register a new application
|
|
686
|
-
const registerNewApplication = async () => {
|
|
687
|
-
// Build source object, only including routerType if it has a value
|
|
688
|
-
const sourceData = {
|
|
689
|
-
type: 'cli',
|
|
690
|
-
projectPathHash,
|
|
691
|
-
cliVersion: pkg.version,
|
|
692
|
-
framework: detection.framework.type || 'unknown',
|
|
693
|
-
frameworkVersion: detection.framework.metadata?.version,
|
|
694
|
-
hasTypeScript: detection.framework.metadata?.hasTypeScript || false,
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
// Only add routerType if it exists
|
|
698
|
-
if (detection.framework.metadata?.routerType) {
|
|
699
|
-
sourceData.routerType = detection.framework.metadata.routerType;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
const registrationData = {
|
|
703
|
-
organizationId,
|
|
704
|
-
name: projectName,
|
|
705
|
-
description: `Connected via CLI from ${detection.framework.type || 'unknown'} project`,
|
|
706
|
-
source: sourceData,
|
|
707
|
-
connection: {
|
|
708
|
-
features,
|
|
709
|
-
hasFrontendDatabase: !!detection.framework.metadata?.hasPrisma,
|
|
710
|
-
frontendDatabaseType: detection.framework.metadata?.hasPrisma ? 'prisma' : undefined,
|
|
711
|
-
},
|
|
712
|
-
deployment: {
|
|
713
|
-
productionUrl: productionDomain ? `https://${productionDomain}` : undefined,
|
|
714
|
-
githubRepo: detection.github.isGitHub ? `${detection.github.owner}/${detection.github.repo}` : undefined,
|
|
715
|
-
githubBranch: detection.github.branch || 'main',
|
|
716
|
-
},
|
|
717
|
-
// Don't generate a new API key if user already has one configured
|
|
718
|
-
skipApiKeyGeneration: apiKey === null,
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
const registrationResult = await backendClient.registerApplication(registrationData);
|
|
722
|
-
return registrationResult;
|
|
723
|
-
};
|
|
724
|
-
|
|
725
|
-
if (existingApp.found && existingApp.application) {
|
|
726
|
-
// Application already registered
|
|
727
|
-
console.log(chalk.yellow(` ⚠️ This project is already registered as "${existingApp.application.name}"`));
|
|
728
|
-
|
|
729
|
-
const { updateAction } = await inquirer.prompt([
|
|
730
|
-
{
|
|
731
|
-
type: 'list',
|
|
732
|
-
name: 'updateAction',
|
|
733
|
-
message: 'What would you like to do?',
|
|
734
|
-
choices: [
|
|
735
|
-
{ name: 'Update existing registration', value: 'update' },
|
|
736
|
-
{ name: 'Register as new application', value: 'new' },
|
|
737
|
-
{ name: 'Skip registration (keep existing)', value: 'skip' },
|
|
738
|
-
],
|
|
739
|
-
},
|
|
740
|
-
]);
|
|
741
|
-
|
|
742
|
-
if (updateAction === 'update') {
|
|
743
|
-
// Update existing application
|
|
744
|
-
const updateData = {
|
|
745
|
-
name: projectName, // Update name too
|
|
746
|
-
connection: {
|
|
747
|
-
features,
|
|
748
|
-
hasFrontendDatabase: !!detection.framework.metadata?.hasPrisma,
|
|
749
|
-
frontendDatabaseType: detection.framework.metadata?.hasPrisma ? 'prisma' : undefined,
|
|
750
|
-
},
|
|
751
|
-
deployment: {
|
|
752
|
-
productionUrl: productionDomain ? `https://${productionDomain}` : undefined,
|
|
753
|
-
githubRepo: detection.github.isGitHub ? `${detection.github.owner}/${detection.github.repo}` : undefined,
|
|
754
|
-
},
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
await backendClient.updateApplication(existingApp.application.id, updateData);
|
|
758
|
-
applicationId = existingApp.application.id;
|
|
759
|
-
isUpdate = true;
|
|
760
|
-
console.log(chalk.green(` ✅ Application registration updated\n`));
|
|
761
|
-
} else if (updateAction === 'new') {
|
|
762
|
-
// Register as new application
|
|
763
|
-
const registrationResult = await registerNewApplication();
|
|
764
|
-
applicationId = registrationResult.applicationId;
|
|
765
|
-
console.log(chalk.green(` ✅ New application registered with L4YERCAK3\n`));
|
|
766
|
-
|
|
767
|
-
if (registrationResult.apiKey && registrationResult.apiKey.key) {
|
|
768
|
-
console.log(chalk.gray(` API key generated: ${registrationResult.apiKey.prefix}`));
|
|
769
|
-
}
|
|
770
|
-
} else {
|
|
771
|
-
applicationId = existingApp.application.id;
|
|
772
|
-
console.log(chalk.gray(` Skipped registration update\n`));
|
|
773
|
-
}
|
|
774
|
-
} else {
|
|
775
|
-
// Register new application
|
|
776
|
-
const registrationResult = await registerNewApplication();
|
|
777
|
-
applicationId = registrationResult.applicationId;
|
|
778
|
-
console.log(chalk.green(` ✅ Application registered with L4YERCAK3\n`));
|
|
779
|
-
|
|
780
|
-
// Show API key if backend generated one
|
|
781
|
-
if (registrationResult.apiKey && registrationResult.apiKey.key) {
|
|
782
|
-
console.log(chalk.gray(` API key: ${registrationResult.apiKey.prefix}`));
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
} catch (regError) {
|
|
786
|
-
// Registration failed but files were generated - warn but don't fail
|
|
787
|
-
console.log(chalk.yellow(` ⚠️ Could not register with backend: ${regError.message}`));
|
|
788
|
-
console.log(chalk.gray(' Your files were generated successfully.'));
|
|
789
|
-
console.log(chalk.gray(' Backend registration will be available in a future update.\n'));
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// Save project configuration
|
|
793
|
-
const projectConfig = {
|
|
794
|
-
organizationId,
|
|
795
|
-
organizationName,
|
|
796
|
-
applicationId,
|
|
797
|
-
projectPathHash,
|
|
798
|
-
apiKey: apiKey ? `${apiKey.substring(0, 10)}...` : '(using existing)', // Store partial key for reference only
|
|
799
|
-
backendUrl,
|
|
800
|
-
features,
|
|
801
|
-
oauthProviders,
|
|
802
|
-
productionDomain,
|
|
803
|
-
frameworkType: detection.framework.type,
|
|
804
|
-
integrationPath,
|
|
805
|
-
selectedDatabase,
|
|
806
|
-
createdAt: Date.now(),
|
|
807
|
-
updatedAt: isUpdate ? Date.now() : undefined,
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
configManager.saveProjectConfig(detection.projectPath, projectConfig);
|
|
811
|
-
console.log(chalk.gray(` 📝 Configuration saved to ~/.l4yercak3/config.json\n`));
|
|
812
|
-
|
|
813
|
-
// Show appropriate completion message based on registration status
|
|
814
|
-
if (applicationId) {
|
|
815
|
-
console.log(chalk.cyan('\n 🎉 Setup complete!\n'));
|
|
816
|
-
} else {
|
|
817
|
-
console.log(chalk.cyan('\n 🎉 Local setup complete!\n'));
|
|
818
|
-
console.log(chalk.yellow(' ⚠️ Note: Backend registration pending - your app works locally but'));
|
|
819
|
-
console.log(chalk.yellow(' won\'t appear in the L4YERCAK3 dashboard until endpoints are available.\n'));
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (features.includes('oauth')) {
|
|
823
|
-
console.log(chalk.yellow(' 📋 Next steps:\n'));
|
|
824
|
-
console.log(chalk.gray(' 1. Follow the OAuth setup guide (OAUTH_SETUP_GUIDE.md)'));
|
|
825
|
-
console.log(chalk.gray(' 2. Add OAuth credentials to .env.local'));
|
|
826
|
-
|
|
827
|
-
// Framework-specific OAuth instructions
|
|
828
|
-
const frameworkType = detection.framework.type;
|
|
829
|
-
if (frameworkType === 'expo' || frameworkType === 'react-native') {
|
|
830
|
-
console.log(chalk.gray(' 3. Install expo-auth-session: npx expo install expo-auth-session expo-crypto'));
|
|
831
|
-
console.log(chalk.gray(' 4. Configure app.json with your OAuth scheme'));
|
|
832
|
-
} else {
|
|
833
|
-
console.log(chalk.gray(' 3. Install NextAuth.js: npm install next-auth'));
|
|
834
|
-
if (oauthProviders.includes('microsoft')) {
|
|
835
|
-
console.log(chalk.gray(' 4. Install Azure AD provider: npm install next-auth/providers/azure-ad'));
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
console.log('');
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
console.log(chalk.gray(' Your project is now connected to L4YERCAK3! 🍰\n'));
|
|
842
|
-
|
|
843
|
-
// Show menu for next actions
|
|
844
|
-
const action = await showMainMenu({ isLoggedIn: true, isInProject: true, hasExistingConfig: true });
|
|
845
|
-
await executeMenuAction(action);
|
|
846
|
-
|
|
847
|
-
} catch (error) {
|
|
848
|
-
console.error(chalk.red(`\n ❌ Error: ${error.message}\n`));
|
|
849
|
-
if (error.stack) {
|
|
850
|
-
console.error(chalk.gray(error.stack));
|
|
851
|
-
}
|
|
852
|
-
process.exit(1);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
module.exports = {
|
|
857
|
-
command: 'spread',
|
|
858
|
-
description: 'Initialize a new project integration',
|
|
859
|
-
handler: handleSpread,
|
|
860
|
-
};
|
|
861
|
-
|