@tpitre/story-ui 2.2.0 ā 2.3.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/.env.sample +82 -11
- package/README.md +89 -0
- package/dist/cli/deploy.d.ts +17 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +696 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +26 -2
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +437 -110
- package/dist/mcp-server/index.d.ts +2 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +120 -2
- package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
- package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
- package/dist/mcp-server/mcp-stdio-server.js +8 -1
- package/dist/mcp-server/routes/claude.d.ts +3 -0
- package/dist/mcp-server/routes/claude.d.ts.map +1 -0
- package/dist/mcp-server/routes/claude.js +60 -23
- package/dist/mcp-server/routes/components.d.ts +4 -0
- package/dist/mcp-server/routes/components.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.d.ts +38 -0
- package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.js +183 -0
- package/dist/mcp-server/routes/generateStory.d.ts +3 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStory.js +160 -76
- package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStoryStream.js +947 -0
- package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
- package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.js +489 -0
- package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
- package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.d.ts +89 -0
- package/dist/mcp-server/routes/providers.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.js +369 -0
- package/dist/mcp-server/routes/storySync.d.ts +26 -0
- package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
- package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.js +18 -0
- package/dist/mcp-server/sessionManager.d.ts +50 -0
- package/dist/mcp-server/sessionManager.d.ts.map +1 -0
- package/dist/story-generator/componentBlacklist.d.ts +21 -0
- package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
- package/dist/story-generator/componentDiscovery.d.ts +28 -0
- package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.js +205 -0
- package/dist/story-generator/configLoader.d.ts +33 -0
- package/dist/story-generator/configLoader.d.ts.map +1 -0
- package/dist/story-generator/considerationsLoader.d.ts +32 -0
- package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
- package/dist/story-generator/documentation-sources.d.ts +28 -0
- package/dist/story-generator/documentation-sources.d.ts.map +1 -0
- package/dist/story-generator/documentationLoader.d.ts +64 -0
- package/dist/story-generator/documentationLoader.d.ts.map +1 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
- package/dist/story-generator/framework-adapters/index.d.ts +97 -0
- package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/index.js +198 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
- package/dist/story-generator/framework-adapters/types.d.ts +182 -0
- package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/types.js +8 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
- package/dist/story-generator/generateStory.d.ts +7 -0
- package/dist/story-generator/generateStory.d.ts.map +1 -0
- package/dist/story-generator/gitignoreManager.d.ts +50 -0
- package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.d.ts +80 -0
- package/dist/story-generator/imageProcessor.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.js +391 -0
- package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
- package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.js +135 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/claude-provider.js +414 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
- package/dist/story-generator/llm-providers/index.d.ts +63 -0
- package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/index.js +169 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/openai-provider.js +458 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/settings-manager.js +173 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
- package/dist/story-generator/llm-providers/types.d.ts +153 -0
- package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/types.js +8 -0
- package/dist/story-generator/logger.d.ts +14 -0
- package/dist/story-generator/logger.d.ts.map +1 -0
- package/dist/story-generator/logger.js +96 -29
- package/dist/story-generator/postProcessStory.d.ts +6 -0
- package/dist/story-generator/postProcessStory.d.ts.map +1 -0
- package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
- package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.d.ts +48 -0
- package/dist/story-generator/promptGenerator.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.js +186 -1
- package/dist/story-generator/storyHistory.d.ts +44 -0
- package/dist/story-generator/storyHistory.d.ts.map +1 -0
- package/dist/story-generator/storySync.d.ts +68 -0
- package/dist/story-generator/storySync.d.ts.map +1 -0
- package/dist/story-generator/storyTracker.d.ts +48 -0
- package/dist/story-generator/storyTracker.d.ts.map +1 -0
- package/dist/story-generator/storyValidator.d.ts +6 -0
- package/dist/story-generator/storyValidator.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
- package/dist/story-generator/urlRedirectService.d.ts +21 -0
- package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
- package/dist/story-generator/validateStory.d.ts +19 -0
- package/dist/story-generator/validateStory.d.ts.map +1 -0
- package/dist/story-generator/validateStory.js +6 -2
- package/dist/story-generator/visionPrompts.d.ts +88 -0
- package/dist/story-generator/visionPrompts.d.ts.map +1 -0
- package/dist/story-generator/visionPrompts.js +462 -0
- package/dist/story-ui.config.d.ts +78 -0
- package/dist/story-ui.config.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
- package/dist/templates/StoryUI/index.d.ts +3 -0
- package/dist/templates/StoryUI/index.d.ts.map +1 -0
- package/dist/templates/StoryUI/index.js +2 -0
- package/package.json +17 -3
- package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
- package/templates/StoryUI/index.tsx +1 -1
- package/templates/StoryUI/manager.tsx +264 -0
- package/templates/production-app/.env.example +11 -0
- package/templates/production-app/index.html +66 -0
- package/templates/production-app/package.json +30 -0
- package/templates/production-app/public/favicon.svg +5 -0
- package/templates/production-app/src/App.tsx +1157 -0
- package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
- package/templates/production-app/src/componentRegistry.ts +315 -0
- package/templates/production-app/src/considerations.ts +16 -0
- package/templates/production-app/src/index.css +284 -0
- package/templates/production-app/src/main.tsx +25 -0
- package/templates/production-app/tsconfig.json +32 -0
- package/templates/production-app/tsconfig.node.json +11 -0
- package/templates/production-app/vite.config.ts +83 -0
- package/templates/react-import-rule.json +2 -2
- package/dist/index.js +0 -12
- package/dist/story-ui.config.loader.js +0 -205
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
function getPackageRoot() {
|
|
9
|
+
// After compilation, __dirname is dist/cli/, so we need to go up two levels
|
|
10
|
+
// to reach the repo root where templates/ and other directories are
|
|
11
|
+
return path.resolve(__dirname, '../..');
|
|
12
|
+
}
|
|
13
|
+
function prompt(question) {
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout
|
|
17
|
+
});
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
rl.question(question, (answer) => {
|
|
20
|
+
rl.close();
|
|
21
|
+
resolve(answer.trim());
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a CLI tool is installed
|
|
27
|
+
*/
|
|
28
|
+
function isToolInstalled(tool) {
|
|
29
|
+
try {
|
|
30
|
+
execSync(`which ${tool}`, { stdio: 'pipe' });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Deploy backend to Railway
|
|
39
|
+
*/
|
|
40
|
+
async function deployToRailway(dryRun) {
|
|
41
|
+
console.log('\nš Deploying backend to Railway...\n');
|
|
42
|
+
const pkgRoot = getPackageRoot();
|
|
43
|
+
// Check dry-run FIRST before any installation or login
|
|
44
|
+
if (dryRun) {
|
|
45
|
+
console.log('[DRY RUN] Would deploy to Railway from:', pkgRoot);
|
|
46
|
+
console.log('[DRY RUN] Required env vars: CLAUDE_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY');
|
|
47
|
+
console.log('[DRY RUN] Prerequisites: Railway CLI, Railway login');
|
|
48
|
+
return 'https://dry-run.railway.app';
|
|
49
|
+
}
|
|
50
|
+
if (!isToolInstalled('railway')) {
|
|
51
|
+
console.log('š¦ Railway CLI not found. Installing...');
|
|
52
|
+
try {
|
|
53
|
+
execSync('npm install -g @railway/cli', { stdio: 'inherit' });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.error('ā Failed to install Railway CLI');
|
|
57
|
+
console.log(' Install manually: npm install -g @railway/cli');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Check if logged in
|
|
62
|
+
try {
|
|
63
|
+
execSync('railway whoami', { stdio: 'pipe' });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
console.log('š Not logged into Railway. Please login:');
|
|
67
|
+
execSync('railway login', { stdio: 'inherit' });
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
// Check if project is linked
|
|
71
|
+
try {
|
|
72
|
+
execSync('railway status', { cwd: pkgRoot, stdio: 'pipe' });
|
|
73
|
+
console.log('ā
Railway project already linked');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
console.log('š Creating new Railway project...');
|
|
77
|
+
execSync('railway init', { cwd: pkgRoot, stdio: 'inherit' });
|
|
78
|
+
}
|
|
79
|
+
// Build the project first
|
|
80
|
+
console.log('šØ Building project...');
|
|
81
|
+
execSync('npm run build', { cwd: pkgRoot, stdio: 'inherit' });
|
|
82
|
+
// Deploy
|
|
83
|
+
console.log('š Deploying to Railway...');
|
|
84
|
+
const result = execSync('railway up --detach 2>&1', {
|
|
85
|
+
cwd: pkgRoot,
|
|
86
|
+
encoding: 'utf-8'
|
|
87
|
+
});
|
|
88
|
+
console.log(result);
|
|
89
|
+
// Get the deployment URL
|
|
90
|
+
console.log('\nš Getting deployment URL...');
|
|
91
|
+
const urlResult = execSync('railway domain 2>&1', {
|
|
92
|
+
cwd: pkgRoot,
|
|
93
|
+
encoding: 'utf-8'
|
|
94
|
+
}).trim();
|
|
95
|
+
if (urlResult && !urlResult.includes('No domain')) {
|
|
96
|
+
console.log(`\nā
Backend deployed to: https://${urlResult}`);
|
|
97
|
+
return `https://${urlResult}`;
|
|
98
|
+
}
|
|
99
|
+
// Try to generate a domain
|
|
100
|
+
console.log('š Generating Railway domain...');
|
|
101
|
+
execSync('railway domain', { cwd: pkgRoot, stdio: 'inherit' });
|
|
102
|
+
const newUrlResult = execSync('railway domain 2>&1', {
|
|
103
|
+
cwd: pkgRoot,
|
|
104
|
+
encoding: 'utf-8'
|
|
105
|
+
}).trim();
|
|
106
|
+
if (newUrlResult) {
|
|
107
|
+
console.log(`\nā
Backend deployed to: https://${newUrlResult}`);
|
|
108
|
+
return `https://${newUrlResult}`;
|
|
109
|
+
}
|
|
110
|
+
console.log('\nā ļø Deployment successful but could not get URL.');
|
|
111
|
+
console.log(' Run "railway domain" to get your deployment URL.');
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error('ā Railway deployment failed:', error.message);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Deploy backend to Render
|
|
121
|
+
*/
|
|
122
|
+
async function deployToRender(dryRun) {
|
|
123
|
+
console.log('\nšØ Deploying backend to Render...\n');
|
|
124
|
+
const pkgRoot = getPackageRoot();
|
|
125
|
+
// Create render.yaml if it doesn't exist
|
|
126
|
+
const renderYamlPath = path.join(pkgRoot, 'render.yaml');
|
|
127
|
+
if (!fs.existsSync(renderYamlPath)) {
|
|
128
|
+
console.log('š Creating render.yaml...');
|
|
129
|
+
const renderYaml = `services:
|
|
130
|
+
- type: web
|
|
131
|
+
name: story-ui-backend
|
|
132
|
+
env: node
|
|
133
|
+
buildCommand: npm install && npm run build
|
|
134
|
+
startCommand: node dist/mcp-server/index.js
|
|
135
|
+
healthCheckPath: /story-ui/providers
|
|
136
|
+
envVars:
|
|
137
|
+
- key: NODE_ENV
|
|
138
|
+
value: production
|
|
139
|
+
- key: PORT
|
|
140
|
+
value: 4001
|
|
141
|
+
- key: CLAUDE_API_KEY
|
|
142
|
+
sync: false
|
|
143
|
+
- key: OPENAI_API_KEY
|
|
144
|
+
sync: false
|
|
145
|
+
- key: GEMINI_API_KEY
|
|
146
|
+
sync: false
|
|
147
|
+
`;
|
|
148
|
+
fs.writeFileSync(renderYamlPath, renderYaml);
|
|
149
|
+
console.log('ā
Created render.yaml');
|
|
150
|
+
}
|
|
151
|
+
if (dryRun) {
|
|
152
|
+
console.log('[DRY RUN] render.yaml created at:', renderYamlPath);
|
|
153
|
+
console.log('[DRY RUN] To deploy:');
|
|
154
|
+
console.log(' 1. Push this repo to GitHub');
|
|
155
|
+
console.log(' 2. Go to https://render.com');
|
|
156
|
+
console.log(' 3. Create new Web Service from your repo');
|
|
157
|
+
console.log(' 4. Set environment variables');
|
|
158
|
+
return 'https://dry-run.onrender.com';
|
|
159
|
+
}
|
|
160
|
+
console.log('\nš Render deployment is Git-based.');
|
|
161
|
+
console.log(' To deploy to Render:\n');
|
|
162
|
+
console.log(' 1. Push your code to GitHub/GitLab');
|
|
163
|
+
console.log(' 2. Go to https://dashboard.render.com');
|
|
164
|
+
console.log(' 3. Click "New" ā "Web Service"');
|
|
165
|
+
console.log(' 4. Connect your repository');
|
|
166
|
+
console.log(' 5. Render will auto-detect the render.yaml config');
|
|
167
|
+
console.log(' 6. Add your API keys as environment variables:');
|
|
168
|
+
console.log(' - CLAUDE_API_KEY');
|
|
169
|
+
console.log(' - OPENAI_API_KEY');
|
|
170
|
+
console.log(' - GEMINI_API_KEY');
|
|
171
|
+
console.log('\n render.yaml has been created in your project root.');
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Deploy backend to Fly.io
|
|
176
|
+
*/
|
|
177
|
+
async function deployToFly(dryRun) {
|
|
178
|
+
console.log('\nšŖ Deploying backend to Fly.io...\n');
|
|
179
|
+
if (!isToolInstalled('flyctl') && !isToolInstalled('fly')) {
|
|
180
|
+
console.log('š¦ Fly CLI not found. Installing...');
|
|
181
|
+
try {
|
|
182
|
+
execSync('curl -L https://fly.io/install.sh | sh', { stdio: 'inherit' });
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
console.error('ā Failed to install Fly CLI');
|
|
186
|
+
console.log(' Install manually: https://fly.io/docs/hands-on/install-flyctl/');
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const pkgRoot = getPackageRoot();
|
|
191
|
+
const flyCmd = isToolInstalled('flyctl') ? 'flyctl' : 'fly';
|
|
192
|
+
// Create fly.toml if it doesn't exist
|
|
193
|
+
const flyTomlPath = path.join(pkgRoot, 'fly.toml');
|
|
194
|
+
if (!fs.existsSync(flyTomlPath)) {
|
|
195
|
+
console.log('š Creating fly.toml...');
|
|
196
|
+
const flyToml = `app = "story-ui-backend"
|
|
197
|
+
primary_region = "sjc"
|
|
198
|
+
|
|
199
|
+
[build]
|
|
200
|
+
dockerfile = "Dockerfile"
|
|
201
|
+
|
|
202
|
+
[http_service]
|
|
203
|
+
internal_port = 4001
|
|
204
|
+
force_https = true
|
|
205
|
+
auto_stop_machines = true
|
|
206
|
+
auto_start_machines = true
|
|
207
|
+
min_machines_running = 0
|
|
208
|
+
|
|
209
|
+
[checks]
|
|
210
|
+
[checks.health]
|
|
211
|
+
grace_period = "30s"
|
|
212
|
+
interval = "30s"
|
|
213
|
+
method = "GET"
|
|
214
|
+
path = "/story-ui/providers"
|
|
215
|
+
timeout = "5s"
|
|
216
|
+
|
|
217
|
+
[env]
|
|
218
|
+
NODE_ENV = "production"
|
|
219
|
+
PORT = "4001"
|
|
220
|
+
`;
|
|
221
|
+
fs.writeFileSync(flyTomlPath, flyToml);
|
|
222
|
+
console.log('ā
Created fly.toml');
|
|
223
|
+
}
|
|
224
|
+
if (dryRun) {
|
|
225
|
+
console.log('[DRY RUN] Would deploy to Fly.io from:', pkgRoot);
|
|
226
|
+
return 'https://dry-run.fly.dev';
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
// Check if logged in
|
|
230
|
+
try {
|
|
231
|
+
execSync(`${flyCmd} auth whoami`, { stdio: 'pipe' });
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
console.log('š Not logged into Fly.io. Please login:');
|
|
235
|
+
execSync(`${flyCmd} auth login`, { stdio: 'inherit' });
|
|
236
|
+
}
|
|
237
|
+
// Launch or deploy
|
|
238
|
+
try {
|
|
239
|
+
execSync(`${flyCmd} status`, { cwd: pkgRoot, stdio: 'pipe' });
|
|
240
|
+
console.log('š Deploying to existing Fly app...');
|
|
241
|
+
execSync(`${flyCmd} deploy`, { cwd: pkgRoot, stdio: 'inherit' });
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
console.log('š Creating new Fly app...');
|
|
245
|
+
execSync(`${flyCmd} launch --no-deploy`, { cwd: pkgRoot, stdio: 'inherit' });
|
|
246
|
+
console.log('\nā ļø Before deploying, set your secrets:');
|
|
247
|
+
console.log(` ${flyCmd} secrets set CLAUDE_API_KEY=your-key`);
|
|
248
|
+
console.log(` ${flyCmd} secrets set OPENAI_API_KEY=your-key`);
|
|
249
|
+
console.log(` ${flyCmd} secrets set GEMINI_API_KEY=your-key`);
|
|
250
|
+
console.log(`\n Then run: ${flyCmd} deploy`);
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
// Get URL
|
|
254
|
+
const appName = execSync(`${flyCmd} status --json 2>/dev/null | grep -o '"Name":"[^"]*"' | head -1 | cut -d'"' -f4`, {
|
|
255
|
+
cwd: pkgRoot,
|
|
256
|
+
encoding: 'utf-8'
|
|
257
|
+
}).trim() || 'story-ui-backend';
|
|
258
|
+
const url = `https://${appName}.fly.dev`;
|
|
259
|
+
console.log(`\nā
Backend deployed to: ${url}`);
|
|
260
|
+
return url;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
console.error('ā Fly.io deployment failed:', error.message);
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Deploy Storybook frontend to Cloudflare Pages
|
|
269
|
+
*/
|
|
270
|
+
async function deployStorybook(backendUrl, storybookDir, projectName, dryRun) {
|
|
271
|
+
console.log('\nš Deploying Storybook to Cloudflare Pages...\n');
|
|
272
|
+
// Check wrangler auth
|
|
273
|
+
try {
|
|
274
|
+
execSync('npx wrangler whoami', { stdio: 'pipe' });
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
console.log('š Not authenticated with Cloudflare.');
|
|
278
|
+
execSync('npx wrangler login', { stdio: 'inherit' });
|
|
279
|
+
}
|
|
280
|
+
// Validate storybook directory
|
|
281
|
+
if (!fs.existsSync(storybookDir)) {
|
|
282
|
+
console.error(`ā Storybook directory not found: ${storybookDir}`);
|
|
283
|
+
console.log(' Make sure you have a Storybook project set up.');
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
const packageJsonPath = path.join(storybookDir, 'package.json');
|
|
287
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
288
|
+
console.error('ā No package.json found in storybook directory');
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
if (dryRun) {
|
|
292
|
+
console.log(`[DRY RUN] Would build Storybook with VITE_STORY_UI_EDGE_URL=${backendUrl}`);
|
|
293
|
+
console.log(`[DRY RUN] Would deploy from: ${storybookDir}`);
|
|
294
|
+
return 'https://dry-run.pages.dev';
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
// Install dependencies if needed
|
|
298
|
+
if (!fs.existsSync(path.join(storybookDir, 'node_modules'))) {
|
|
299
|
+
console.log('š¦ Installing dependencies...');
|
|
300
|
+
execSync('npm install', { cwd: storybookDir, stdio: 'inherit' });
|
|
301
|
+
}
|
|
302
|
+
// Build storybook with the backend URL
|
|
303
|
+
console.log(`šØ Building Storybook with backend URL: ${backendUrl}`);
|
|
304
|
+
execSync(`VITE_STORY_UI_EDGE_URL=${backendUrl} npm run build-storybook`, {
|
|
305
|
+
cwd: storybookDir,
|
|
306
|
+
stdio: 'inherit'
|
|
307
|
+
});
|
|
308
|
+
// Deploy to Cloudflare Pages
|
|
309
|
+
const staticDir = path.join(storybookDir, 'storybook-static');
|
|
310
|
+
if (!fs.existsSync(staticDir)) {
|
|
311
|
+
console.error('ā storybook-static directory not found after build');
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
console.log('š Deploying to Cloudflare Pages...');
|
|
315
|
+
const result = execSync(`npx wrangler pages deploy ${staticDir} --project-name=${projectName}-storybook 2>&1`, {
|
|
316
|
+
encoding: 'utf-8'
|
|
317
|
+
});
|
|
318
|
+
console.log(result);
|
|
319
|
+
// Extract URL from output
|
|
320
|
+
const urlMatch = result.match(/https:\/\/[^\s]+\.pages\.dev/);
|
|
321
|
+
if (urlMatch) {
|
|
322
|
+
console.log(`\nā
Storybook deployed to: ${urlMatch[0]}`);
|
|
323
|
+
return urlMatch[0];
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
console.error('ā Storybook deployment failed:', error.message);
|
|
329
|
+
if (error.stdout)
|
|
330
|
+
console.log(error.stdout);
|
|
331
|
+
if (error.stderr)
|
|
332
|
+
console.error(error.stderr);
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Build and deploy standalone production app (Lovable/Bolt-style)
|
|
338
|
+
* This builds a React app with the user's component library bundled in
|
|
339
|
+
*/
|
|
340
|
+
async function deployProductionApp(backendUrl, projectName, dryRun) {
|
|
341
|
+
console.log('\nš Building Standalone Production App...\n');
|
|
342
|
+
console.log(' This creates a Lovable/Bolt-style UI with your component library\n');
|
|
343
|
+
const pkgRoot = getPackageRoot();
|
|
344
|
+
const userCwd = process.cwd();
|
|
345
|
+
const templateDir = path.join(pkgRoot, 'templates/production-app');
|
|
346
|
+
const buildDir = path.join(userCwd, '.story-ui-build');
|
|
347
|
+
// Check for story-ui.config.js in user's project
|
|
348
|
+
const configPath = path.join(userCwd, 'story-ui.config.js');
|
|
349
|
+
if (!fs.existsSync(configPath)) {
|
|
350
|
+
console.error('ā No story-ui.config.js found in current directory');
|
|
351
|
+
console.log(' Run "npx story-ui init" first to configure your component library');
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (dryRun) {
|
|
355
|
+
console.log(`[DRY RUN] Would build production app from: ${templateDir}`);
|
|
356
|
+
console.log(`[DRY RUN] Would bundle with components from: ${configPath}`);
|
|
357
|
+
console.log(`[DRY RUN] Backend URL would be: ${backendUrl}`);
|
|
358
|
+
return 'https://dry-run.pages.dev';
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
// 1. Copy template to build directory
|
|
362
|
+
console.log('š Setting up build directory...');
|
|
363
|
+
if (fs.existsSync(buildDir)) {
|
|
364
|
+
fs.rmSync(buildDir, { recursive: true });
|
|
365
|
+
}
|
|
366
|
+
fs.mkdirSync(buildDir, { recursive: true });
|
|
367
|
+
// Copy template files
|
|
368
|
+
copyDirectory(templateDir, buildDir);
|
|
369
|
+
// 2. Load user config and discover components
|
|
370
|
+
console.log('š Discovering components from your library...');
|
|
371
|
+
const { generateComponentRegistry } = await import('../story-generator/componentRegistryGenerator.js');
|
|
372
|
+
const registryOutputPath = path.join(buildDir, 'src/componentRegistry.ts');
|
|
373
|
+
// Generate the component registry in the user's project context
|
|
374
|
+
process.chdir(userCwd);
|
|
375
|
+
await generateComponentRegistry(registryOutputPath);
|
|
376
|
+
// 2.5. Generate AI considerations from story-ui-considerations.md (if exists)
|
|
377
|
+
console.log('š Loading AI considerations...');
|
|
378
|
+
const { loadConsiderations, considerationsToPrompt } = await import('../story-generator/considerationsLoader.js');
|
|
379
|
+
const considerations = loadConsiderations(); // Auto-finds in common locations
|
|
380
|
+
const considerationsPrompt = considerations ? considerationsToPrompt(considerations) : '';
|
|
381
|
+
const considerationsOutputPath = path.join(buildDir, 'src/considerations.ts');
|
|
382
|
+
// Write considerations to a TypeScript file
|
|
383
|
+
const considerationsContent = `/**
|
|
384
|
+
* AI Considerations - Auto-generated from story-ui-considerations.md
|
|
385
|
+
*
|
|
386
|
+
* This file contains design-system-specific instructions for the AI
|
|
387
|
+
* when generating components. Edit story-ui-considerations.md in your
|
|
388
|
+
* project root to customize these rules.
|
|
389
|
+
*/
|
|
390
|
+
|
|
391
|
+
export const aiConsiderations = ${JSON.stringify(considerationsPrompt, null, 2)};
|
|
392
|
+
|
|
393
|
+
export const hasConsiderations = ${considerations ? 'true' : 'false'};
|
|
394
|
+
`;
|
|
395
|
+
fs.writeFileSync(considerationsOutputPath, considerationsContent);
|
|
396
|
+
console.log(considerations
|
|
397
|
+
? `ā
Loaded AI considerations for: ${considerations.libraryName || 'your component library'}`
|
|
398
|
+
: 'ā ļø No story-ui-considerations.md found - using default prompts');
|
|
399
|
+
process.chdir(buildDir);
|
|
400
|
+
// 3. Install dependencies
|
|
401
|
+
console.log('š¦ Installing dependencies...');
|
|
402
|
+
// Read the user's config
|
|
403
|
+
const userConfig = await import(configPath);
|
|
404
|
+
const config = userConfig.default;
|
|
405
|
+
const componentLibraryPackage = config.importPath;
|
|
406
|
+
// Update package.json to include the component library as a dependency
|
|
407
|
+
const packageJsonPath = path.join(buildDir, 'package.json');
|
|
408
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
409
|
+
// Add dependencies from user config (design system agnostic)
|
|
410
|
+
const packagesToAdd = config.dependencies || [componentLibraryPackage];
|
|
411
|
+
for (const pkg of packagesToAdd) {
|
|
412
|
+
packageJson.dependencies[pkg] = '*';
|
|
413
|
+
}
|
|
414
|
+
// Add any additional imports as dependencies
|
|
415
|
+
if (config.additionalImports) {
|
|
416
|
+
for (const imp of config.additionalImports) {
|
|
417
|
+
packageJson.dependencies[imp.path] = '*';
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
421
|
+
// 3.5. Generate main.tsx with user-defined provider (design system agnostic)
|
|
422
|
+
console.log('āļø Configuring app providers from story-ui.config.js...');
|
|
423
|
+
const mainTsxPath = path.join(buildDir, 'src/main.tsx');
|
|
424
|
+
const mainTsxContent = generateMainTsxFromConfig(config);
|
|
425
|
+
fs.writeFileSync(mainTsxPath, mainTsxContent);
|
|
426
|
+
// Install deps using npm with link to use local versions
|
|
427
|
+
execSync('npm install', { cwd: buildDir, stdio: 'inherit' });
|
|
428
|
+
// 4. Create .env file with backend URL
|
|
429
|
+
console.log('āļø Configuring backend URL...');
|
|
430
|
+
fs.writeFileSync(path.join(buildDir, '.env'), `VITE_BACKEND_URL=${backendUrl}\nVITE_APP_TITLE=Story UI\n`);
|
|
431
|
+
// 5. Build the app
|
|
432
|
+
console.log('šØ Building production app...');
|
|
433
|
+
execSync('npm run build', { cwd: buildDir, stdio: 'inherit' });
|
|
434
|
+
// 6. Deploy to Cloudflare Pages
|
|
435
|
+
console.log('āļø Deploying to Cloudflare Pages...');
|
|
436
|
+
// Check wrangler auth
|
|
437
|
+
try {
|
|
438
|
+
execSync('npx wrangler whoami', { stdio: 'pipe' });
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
console.log('š Not authenticated with Cloudflare.');
|
|
442
|
+
execSync('npx wrangler login', { stdio: 'inherit' });
|
|
443
|
+
}
|
|
444
|
+
const distDir = path.join(buildDir, 'dist');
|
|
445
|
+
const result = execSync(`npx wrangler pages deploy ${distDir} --project-name=${projectName}-app 2>&1`, {
|
|
446
|
+
encoding: 'utf-8'
|
|
447
|
+
});
|
|
448
|
+
console.log(result);
|
|
449
|
+
// Extract URL from output
|
|
450
|
+
const urlMatch = result.match(/https:\/\/[^\s]+\.pages\.dev/);
|
|
451
|
+
if (urlMatch) {
|
|
452
|
+
console.log(`\nā
Production app deployed to: ${urlMatch[0]}`);
|
|
453
|
+
return urlMatch[0];
|
|
454
|
+
}
|
|
455
|
+
console.log('\nā ļø Deployment completed but URL not detected in output.');
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
console.error('ā Production app deployment failed:', error.message);
|
|
460
|
+
if (error.stdout)
|
|
461
|
+
console.log(error.stdout);
|
|
462
|
+
if (error.stderr)
|
|
463
|
+
console.error(error.stderr);
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
finally {
|
|
467
|
+
// Return to original directory
|
|
468
|
+
process.chdir(userCwd);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Generate main.tsx from user config (design system agnostic)
|
|
473
|
+
*
|
|
474
|
+
* User's story-ui.config.js can define provider configuration:
|
|
475
|
+
* {
|
|
476
|
+
* provider: {
|
|
477
|
+
* cssImports: ["your-library/styles.css"], // CSS files to import
|
|
478
|
+
* imports: ["import { YourProvider } from 'your-library';"], // Provider imports
|
|
479
|
+
* wrapper: "<YourProvider>{children}</YourProvider>" // JSX wrapper with {children} placeholder
|
|
480
|
+
* }
|
|
481
|
+
* }
|
|
482
|
+
*/
|
|
483
|
+
function generateMainTsxFromConfig(config) {
|
|
484
|
+
const provider = config.provider || {};
|
|
485
|
+
// CSS imports (if any)
|
|
486
|
+
const cssImports = (provider.cssImports || [])
|
|
487
|
+
.map((css) => `import '${css}';`)
|
|
488
|
+
.join('\n');
|
|
489
|
+
// Provider component imports (if any)
|
|
490
|
+
const providerImports = (provider.imports || []).join('\n');
|
|
491
|
+
// Provider wrapper - replace {children} with <App />
|
|
492
|
+
let appElement = '<App />';
|
|
493
|
+
if (provider.wrapper) {
|
|
494
|
+
appElement = provider.wrapper.replace('{children}', '<App />');
|
|
495
|
+
}
|
|
496
|
+
return `/**
|
|
497
|
+
* Production App Entry Point
|
|
498
|
+
*
|
|
499
|
+
* This is the main entry point for the Story UI production app.
|
|
500
|
+
* Provider configuration is read from story-ui.config.js
|
|
501
|
+
*/
|
|
502
|
+
|
|
503
|
+
import React from 'react';
|
|
504
|
+
import ReactDOM from 'react-dom/client';
|
|
505
|
+
${cssImports}
|
|
506
|
+
${providerImports}
|
|
507
|
+
import App from './App';
|
|
508
|
+
import './index.css';
|
|
509
|
+
|
|
510
|
+
// Mount the app
|
|
511
|
+
const rootElement = document.getElementById('root');
|
|
512
|
+
|
|
513
|
+
if (!rootElement) {
|
|
514
|
+
throw new Error('Root element not found. Make sure there is a <div id="root"></div> in your HTML.');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
ReactDOM.createRoot(rootElement).render(
|
|
518
|
+
<React.StrictMode>
|
|
519
|
+
${appElement}
|
|
520
|
+
</React.StrictMode>
|
|
521
|
+
);
|
|
522
|
+
`;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Helper function to copy directory recursively
|
|
526
|
+
*/
|
|
527
|
+
function copyDirectory(src, dest) {
|
|
528
|
+
if (!fs.existsSync(dest)) {
|
|
529
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
530
|
+
}
|
|
531
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
532
|
+
for (const entry of entries) {
|
|
533
|
+
const srcPath = path.join(src, entry.name);
|
|
534
|
+
const destPath = path.join(dest, entry.name);
|
|
535
|
+
if (entry.isDirectory()) {
|
|
536
|
+
copyDirectory(srcPath, destPath);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
fs.copyFileSync(srcPath, destPath);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Print deployment summary
|
|
545
|
+
*/
|
|
546
|
+
function printSummary(backendUrl, frontendUrl, appUrl) {
|
|
547
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
548
|
+
console.log('š DEPLOYMENT SUMMARY');
|
|
549
|
+
console.log('ā'.repeat(60));
|
|
550
|
+
if (backendUrl) {
|
|
551
|
+
console.log(`\nš„ļø Backend API: ${backendUrl}`);
|
|
552
|
+
console.log(' Endpoints:');
|
|
553
|
+
console.log(` - ${backendUrl}/story-ui/providers`);
|
|
554
|
+
console.log(` - ${backendUrl}/story-ui/generate-stream`);
|
|
555
|
+
console.log(` - ${backendUrl}/story-ui/stories`);
|
|
556
|
+
}
|
|
557
|
+
if (appUrl) {
|
|
558
|
+
console.log(`\nš Production App: ${appUrl}`);
|
|
559
|
+
console.log(' This is your Lovable/Bolt-style UI with your component library');
|
|
560
|
+
console.log(' Users can prompt and see live-rendered components!');
|
|
561
|
+
}
|
|
562
|
+
if (frontendUrl) {
|
|
563
|
+
console.log(`\nš Storybook UI: ${frontendUrl}`);
|
|
564
|
+
console.log(' (Legacy Storybook-based interface)');
|
|
565
|
+
}
|
|
566
|
+
if (backendUrl && (appUrl || frontendUrl)) {
|
|
567
|
+
console.log('\nā
Full deployment complete!');
|
|
568
|
+
console.log(' Non-developers can now access Story UI at:');
|
|
569
|
+
if (appUrl) {
|
|
570
|
+
console.log(` ${appUrl} (Recommended)`);
|
|
571
|
+
}
|
|
572
|
+
if (frontendUrl) {
|
|
573
|
+
console.log(` ${frontendUrl} (Storybook)`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
console.log('\n' + 'ā'.repeat(60));
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Legacy Cloudflare Edge deployment (deprecated)
|
|
580
|
+
*/
|
|
581
|
+
async function legacyEdgeDeployment(options) {
|
|
582
|
+
console.log('\nā ļø WARNING: The Edge Worker deployment is deprecated.');
|
|
583
|
+
console.log(' It\'s recommended to use the new approach instead:');
|
|
584
|
+
console.log(' npx story-ui deploy --backend --platform=railway');
|
|
585
|
+
console.log(' npx story-ui deploy --frontend --backend-url=<your-backend-url>\n');
|
|
586
|
+
const answer = await prompt('Continue with legacy deployment? (y/N): ');
|
|
587
|
+
if (answer.toLowerCase() !== 'y') {
|
|
588
|
+
console.log('Aborted. Use --backend and --frontend for the new approach.');
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// Original legacy code here...
|
|
592
|
+
console.log('Legacy deployment not implemented in new CLI.');
|
|
593
|
+
console.log('Please use the new --backend and --frontend flags.');
|
|
594
|
+
}
|
|
595
|
+
export async function deployCommand(options) {
|
|
596
|
+
console.log('\nāļø Story UI Production Deployment');
|
|
597
|
+
console.log('ā'.repeat(40) + '\n');
|
|
598
|
+
// Handle legacy flags
|
|
599
|
+
if (options.edge || options.pages || options.all || options.init) {
|
|
600
|
+
await legacyEdgeDeployment(options);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
// New recommended deployment flow
|
|
604
|
+
let backendUrl = options.backendUrl || null;
|
|
605
|
+
let frontendUrl = null;
|
|
606
|
+
let appUrl = null;
|
|
607
|
+
// Deploy backend
|
|
608
|
+
if (options.backend) {
|
|
609
|
+
const platform = options.platform || 'railway';
|
|
610
|
+
switch (platform) {
|
|
611
|
+
case 'railway':
|
|
612
|
+
backendUrl = await deployToRailway(options.dryRun || false);
|
|
613
|
+
break;
|
|
614
|
+
case 'render':
|
|
615
|
+
backendUrl = await deployToRender(options.dryRun || false);
|
|
616
|
+
break;
|
|
617
|
+
case 'fly':
|
|
618
|
+
backendUrl = await deployToFly(options.dryRun || false);
|
|
619
|
+
break;
|
|
620
|
+
default:
|
|
621
|
+
console.error(`ā Unknown platform: ${platform}`);
|
|
622
|
+
console.log(' Supported platforms: railway, render, fly');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (!backendUrl && !options.dryRun) {
|
|
626
|
+
console.log('\nā ļø Backend deployment did not return a URL.');
|
|
627
|
+
console.log(' You may need to check the platform dashboard for the URL.');
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Deploy standalone production app (Lovable/Bolt-style)
|
|
631
|
+
if (options.app) {
|
|
632
|
+
if (!backendUrl) {
|
|
633
|
+
backendUrl = await prompt('Enter your backend URL: ');
|
|
634
|
+
if (!backendUrl) {
|
|
635
|
+
console.error('ā Backend URL is required for app deployment.');
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const projectName = options.projectName || 'story-ui';
|
|
640
|
+
appUrl = await deployProductionApp(backendUrl, projectName, options.dryRun || false);
|
|
641
|
+
}
|
|
642
|
+
// Deploy frontend (legacy Storybook-based)
|
|
643
|
+
if (options.frontend) {
|
|
644
|
+
if (!backendUrl) {
|
|
645
|
+
backendUrl = await prompt('Enter your backend URL: ');
|
|
646
|
+
if (!backendUrl) {
|
|
647
|
+
console.error('ā Backend URL is required for frontend deployment.');
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
const storybookDir = options.storybookDir || process.cwd();
|
|
652
|
+
const projectName = options.projectName || 'story-ui';
|
|
653
|
+
frontendUrl = await deployStorybook(backendUrl, storybookDir, projectName, options.dryRun || false);
|
|
654
|
+
}
|
|
655
|
+
// Print summary
|
|
656
|
+
if (options.backend || options.frontend || options.app) {
|
|
657
|
+
printSummary(backendUrl, frontendUrl, appUrl);
|
|
658
|
+
}
|
|
659
|
+
// Show help if no flags provided
|
|
660
|
+
if (!options.backend && !options.frontend && !options.app && !options.edge && !options.pages && !options.all && !options.init) {
|
|
661
|
+
console.log('Story UI Deployment - Deploy your component library as a production app\n');
|
|
662
|
+
console.log('RECOMMENDED APPROACH (Lovable/Bolt-style App):');
|
|
663
|
+
console.log('ā'.repeat(50));
|
|
664
|
+
console.log(' --backend Deploy MCP server backend');
|
|
665
|
+
console.log(' --app Deploy standalone production app (RECOMMENDED)');
|
|
666
|
+
console.log(' Builds a Lovable/Bolt-style UI with your components');
|
|
667
|
+
console.log(' --platform <name> Backend platform: railway (default), render, fly');
|
|
668
|
+
console.log(' --backend-url <url> Use existing backend URL');
|
|
669
|
+
console.log(' --project-name <name> Project name prefix (default: story-ui)');
|
|
670
|
+
console.log(' --dry-run Show what would be deployed\n');
|
|
671
|
+
console.log('EXAMPLES:');
|
|
672
|
+
console.log('ā'.repeat(50));
|
|
673
|
+
console.log(' # Deploy everything (recommended)');
|
|
674
|
+
console.log(' npx story-ui deploy --backend --app\n');
|
|
675
|
+
console.log(' # Deploy backend only to Railway');
|
|
676
|
+
console.log(' npx story-ui deploy --backend --platform=railway\n');
|
|
677
|
+
console.log(' # Deploy app with existing backend');
|
|
678
|
+
console.log(' npx story-ui deploy --app --backend-url=https://your-api.railway.app\n');
|
|
679
|
+
console.log(' # Custom project name');
|
|
680
|
+
console.log(' npx story-ui deploy --backend --app --project-name=my-design-system\n');
|
|
681
|
+
console.log('ENVIRONMENT VARIABLES (set on backend platform):');
|
|
682
|
+
console.log('ā'.repeat(50));
|
|
683
|
+
console.log(' CLAUDE_API_KEY - Anthropic API key');
|
|
684
|
+
console.log(' OPENAI_API_KEY - OpenAI API key');
|
|
685
|
+
console.log(' GEMINI_API_KEY - Google Gemini API key');
|
|
686
|
+
console.log(' (Set at least one of these)\n');
|
|
687
|
+
console.log('LEGACY OPTIONS (Storybook-based frontend):');
|
|
688
|
+
console.log('ā'.repeat(50));
|
|
689
|
+
console.log(' --frontend Deploy Storybook frontend (use --app instead)');
|
|
690
|
+
console.log(' --storybook-dir <dir> Path to Storybook project\n');
|
|
691
|
+
console.log('DEPRECATED OPTIONS (Cloudflare Edge approach):');
|
|
692
|
+
console.log('ā'.repeat(50));
|
|
693
|
+
console.log(' --init, --edge, --pages, --all');
|
|
694
|
+
console.log(' These are deprecated. Use --backend and --app instead.\n');
|
|
695
|
+
}
|
|
696
|
+
}
|