@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.
Files changed (188) hide show
  1. package/.env.sample +82 -11
  2. package/README.md +89 -0
  3. package/dist/cli/deploy.d.ts +17 -0
  4. package/dist/cli/deploy.d.ts.map +1 -0
  5. package/dist/cli/deploy.js +696 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +26 -2
  9. package/dist/cli/setup.d.ts +11 -0
  10. package/dist/cli/setup.d.ts.map +1 -0
  11. package/dist/cli/setup.js +437 -110
  12. package/dist/mcp-server/index.d.ts +2 -0
  13. package/dist/mcp-server/index.d.ts.map +1 -0
  14. package/dist/mcp-server/index.js +120 -2
  15. package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
  16. package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
  17. package/dist/mcp-server/mcp-stdio-server.js +8 -1
  18. package/dist/mcp-server/routes/claude.d.ts +3 -0
  19. package/dist/mcp-server/routes/claude.d.ts.map +1 -0
  20. package/dist/mcp-server/routes/claude.js +60 -23
  21. package/dist/mcp-server/routes/components.d.ts +4 -0
  22. package/dist/mcp-server/routes/components.d.ts.map +1 -0
  23. package/dist/mcp-server/routes/frameworks.d.ts +38 -0
  24. package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
  25. package/dist/mcp-server/routes/frameworks.js +183 -0
  26. package/dist/mcp-server/routes/generateStory.d.ts +3 -0
  27. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
  28. package/dist/mcp-server/routes/generateStory.js +160 -76
  29. package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
  30. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
  31. package/dist/mcp-server/routes/generateStoryStream.js +947 -0
  32. package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
  33. package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
  34. package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
  35. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
  36. package/dist/mcp-server/routes/mcpRemote.js +489 -0
  37. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  38. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  39. package/dist/mcp-server/routes/providers.d.ts +89 -0
  40. package/dist/mcp-server/routes/providers.d.ts.map +1 -0
  41. package/dist/mcp-server/routes/providers.js +369 -0
  42. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  43. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  44. package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
  45. package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
  46. package/dist/mcp-server/routes/streamTypes.js +18 -0
  47. package/dist/mcp-server/sessionManager.d.ts +50 -0
  48. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  49. package/dist/story-generator/componentBlacklist.d.ts +21 -0
  50. package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
  51. package/dist/story-generator/componentDiscovery.d.ts +28 -0
  52. package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
  53. package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
  54. package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
  55. package/dist/story-generator/componentRegistryGenerator.js +205 -0
  56. package/dist/story-generator/configLoader.d.ts +33 -0
  57. package/dist/story-generator/configLoader.d.ts.map +1 -0
  58. package/dist/story-generator/considerationsLoader.d.ts +32 -0
  59. package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
  60. package/dist/story-generator/documentation-sources.d.ts +28 -0
  61. package/dist/story-generator/documentation-sources.d.ts.map +1 -0
  62. package/dist/story-generator/documentationLoader.d.ts +64 -0
  63. package/dist/story-generator/documentationLoader.d.ts.map +1 -0
  64. package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
  65. package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
  66. package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
  67. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
  68. package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
  69. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
  70. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
  71. package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
  72. package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
  73. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
  74. package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
  75. package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
  76. package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
  77. package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
  78. package/dist/story-generator/framework-adapters/index.d.ts +97 -0
  79. package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
  80. package/dist/story-generator/framework-adapters/index.js +198 -0
  81. package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
  82. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
  83. package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
  84. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
  85. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
  86. package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
  87. package/dist/story-generator/framework-adapters/types.d.ts +182 -0
  88. package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
  89. package/dist/story-generator/framework-adapters/types.js +8 -0
  90. package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
  91. package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
  92. package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
  93. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
  94. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
  95. package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
  96. package/dist/story-generator/generateStory.d.ts +7 -0
  97. package/dist/story-generator/generateStory.d.ts.map +1 -0
  98. package/dist/story-generator/gitignoreManager.d.ts +50 -0
  99. package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
  100. package/dist/story-generator/imageProcessor.d.ts +80 -0
  101. package/dist/story-generator/imageProcessor.d.ts.map +1 -0
  102. package/dist/story-generator/imageProcessor.js +391 -0
  103. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  104. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  105. package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
  106. package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
  107. package/dist/story-generator/llm-providers/base-provider.js +135 -0
  108. package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
  109. package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
  110. package/dist/story-generator/llm-providers/claude-provider.js +414 -0
  111. package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
  112. package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
  113. package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
  114. package/dist/story-generator/llm-providers/index.d.ts +63 -0
  115. package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
  116. package/dist/story-generator/llm-providers/index.js +169 -0
  117. package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
  118. package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
  119. package/dist/story-generator/llm-providers/openai-provider.js +458 -0
  120. package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
  121. package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
  122. package/dist/story-generator/llm-providers/settings-manager.js +173 -0
  123. package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
  124. package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
  125. package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
  126. package/dist/story-generator/llm-providers/types.d.ts +153 -0
  127. package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
  128. package/dist/story-generator/llm-providers/types.js +8 -0
  129. package/dist/story-generator/logger.d.ts +14 -0
  130. package/dist/story-generator/logger.d.ts.map +1 -0
  131. package/dist/story-generator/logger.js +96 -29
  132. package/dist/story-generator/postProcessStory.d.ts +6 -0
  133. package/dist/story-generator/postProcessStory.d.ts.map +1 -0
  134. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  135. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  136. package/dist/story-generator/promptGenerator.d.ts +48 -0
  137. package/dist/story-generator/promptGenerator.d.ts.map +1 -0
  138. package/dist/story-generator/promptGenerator.js +186 -1
  139. package/dist/story-generator/storyHistory.d.ts +44 -0
  140. package/dist/story-generator/storyHistory.d.ts.map +1 -0
  141. package/dist/story-generator/storySync.d.ts +68 -0
  142. package/dist/story-generator/storySync.d.ts.map +1 -0
  143. package/dist/story-generator/storyTracker.d.ts +48 -0
  144. package/dist/story-generator/storyTracker.d.ts.map +1 -0
  145. package/dist/story-generator/storyValidator.d.ts +6 -0
  146. package/dist/story-generator/storyValidator.d.ts.map +1 -0
  147. package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
  148. package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
  149. package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
  150. package/dist/story-generator/urlRedirectService.d.ts +21 -0
  151. package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
  152. package/dist/story-generator/validateStory.d.ts +19 -0
  153. package/dist/story-generator/validateStory.d.ts.map +1 -0
  154. package/dist/story-generator/validateStory.js +6 -2
  155. package/dist/story-generator/visionPrompts.d.ts +88 -0
  156. package/dist/story-generator/visionPrompts.d.ts.map +1 -0
  157. package/dist/story-generator/visionPrompts.js +462 -0
  158. package/dist/story-ui.config.d.ts +78 -0
  159. package/dist/story-ui.config.d.ts.map +1 -0
  160. package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
  161. package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
  162. package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
  163. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
  164. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
  165. package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  166. package/dist/templates/StoryUI/index.d.ts +3 -0
  167. package/dist/templates/StoryUI/index.d.ts.map +1 -0
  168. package/dist/templates/StoryUI/index.js +2 -0
  169. package/package.json +17 -3
  170. package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
  171. package/templates/StoryUI/index.tsx +1 -1
  172. package/templates/StoryUI/manager.tsx +264 -0
  173. package/templates/production-app/.env.example +11 -0
  174. package/templates/production-app/index.html +66 -0
  175. package/templates/production-app/package.json +30 -0
  176. package/templates/production-app/public/favicon.svg +5 -0
  177. package/templates/production-app/src/App.tsx +1157 -0
  178. package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
  179. package/templates/production-app/src/componentRegistry.ts +315 -0
  180. package/templates/production-app/src/considerations.ts +16 -0
  181. package/templates/production-app/src/index.css +284 -0
  182. package/templates/production-app/src/main.tsx +25 -0
  183. package/templates/production-app/tsconfig.json +32 -0
  184. package/templates/production-app/tsconfig.node.json +11 -0
  185. package/templates/production-app/vite.config.ts +83 -0
  186. package/templates/react-import-rule.json +2 -2
  187. package/dist/index.js +0 -12
  188. 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
+ }