vibefast-cli 0.7.12 → 0.7.14

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 (97) hide show
  1. package/dist/commands/add.d.ts.map +1 -1
  2. package/dist/commands/add.js +28 -2
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +5 -3
  6. package/dist/commands/init.js.map +1 -1
  7. package/package.json +1 -1
  8. package/recipes/audio-recorder/recipe.json +1 -1
  9. package/recipes/audio-recorder@latest.zip +0 -0
  10. package/recipes/charts/apps/native/src/app/{charts → (root)/(protected)/charts}/index.tsx +0 -3
  11. package/recipes/charts/apps/native/src/features/charts/app/preview.tsx +0 -3
  12. package/recipes/charts/apps/native/src/features/charts/components/area-chart.tsx +0 -3
  13. package/recipes/charts/apps/native/src/features/charts/components/bar-chart.tsx +0 -3
  14. package/recipes/charts/apps/native/src/features/charts/components/candlestick-chart.tsx +0 -3
  15. package/recipes/charts/apps/native/src/features/charts/components/chart-card.tsx +0 -3
  16. package/recipes/charts/apps/native/src/features/charts/components/column-chart.tsx +0 -3
  17. package/recipes/charts/apps/native/src/features/charts/components/doughnut-chart.tsx +0 -3
  18. package/recipes/charts/apps/native/src/features/charts/components/index.ts +0 -3
  19. package/recipes/charts/apps/native/src/features/charts/components/line-chart.tsx +0 -3
  20. package/recipes/charts/apps/native/src/features/charts/components/radar-chart.tsx +0 -3
  21. package/recipes/charts/apps/native/src/features/charts/components/radial-bar-chart.tsx +0 -3
  22. package/recipes/charts/apps/native/src/features/charts/components/stacked-area-chart.tsx +0 -3
  23. package/recipes/charts/apps/native/src/features/charts/components/stacked-bar-chart.tsx +0 -3
  24. package/recipes/charts/apps/native/src/features/charts/data/mock-data.ts +0 -3
  25. package/recipes/charts/apps/native/src/features/charts/types/index.ts +0 -3
  26. package/recipes/charts/recipe.json +1 -1
  27. package/recipes/charts@latest.zip +0 -0
  28. package/recipes/chatbot/apps/native/src/api-client/chatbot.ts +83 -0
  29. package/recipes/chatbot/apps/native/src/app/{chatbot → (root)/(protected)/chatbot}/index.tsx +0 -1
  30. package/recipes/chatbot/apps/native/src/features/chatbot/app/index.tsx +56 -60
  31. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-header-buttons.tsx +0 -1
  32. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-input-bar.tsx +0 -1
  33. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-markdown.tsx +0 -1
  34. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-message-bubble.tsx +3 -26
  35. package/recipes/chatbot/apps/native/src/features/chatbot/components/chat-settings-modal.tsx +0 -1
  36. package/recipes/chatbot/apps/native/src/features/chatbot/components/image-preview-list.tsx +0 -1
  37. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/code-block.tsx +0 -1
  38. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/index.ts +0 -1
  39. package/recipes/chatbot/apps/native/src/features/chatbot/components/markdown/table-renderer.tsx +0 -1
  40. package/recipes/chatbot/apps/native/src/features/chatbot/components/message-error-boundary.tsx +0 -1
  41. package/recipes/chatbot/apps/native/src/features/chatbot/components/message-list.tsx +10 -14
  42. package/recipes/chatbot/apps/native/src/features/chatbot/components/model-selector.tsx +0 -1
  43. package/recipes/chatbot/apps/native/src/features/chatbot/components/report-content-modal.tsx +0 -1
  44. package/recipes/chatbot/apps/native/src/features/chatbot/components/suggested-messages.tsx +0 -1
  45. package/recipes/chatbot/apps/native/src/features/chatbot/constants/models.ts +0 -1
  46. package/recipes/chatbot/apps/native/src/features/chatbot/constants/report-reasons.ts +0 -1
  47. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-attachment-cache.ts +0 -1
  48. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-chat-config.ts +0 -1
  49. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-chat-handlers.ts +0 -1
  50. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-chatbot-settings.ts +0 -1
  51. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-conversation.ts +0 -1
  52. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-image-picker.ts +0 -1
  53. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-keyboard-coordinator.ts +0 -1
  54. package/recipes/chatbot/apps/native/src/features/chatbot/hooks/use-smart-scroll-manager.ts +0 -1
  55. package/recipes/chatbot/apps/native/src/features/chatbot/models/index.ts +0 -1
  56. package/recipes/chatbot/apps/native/src/features/chatbot/models/models.ts +0 -1
  57. package/recipes/chatbot/apps/native/src/features/chatbot/models/providers.ts +0 -1
  58. package/recipes/chatbot/apps/native/src/features/chatbot/models/types.ts +0 -1
  59. package/recipes/chatbot/apps/native/src/features/chatbot/services/file-uploader.ts +0 -1
  60. package/recipes/chatbot/apps/native/src/features/chatbot/services/message-handler-service.ts +0 -1
  61. package/recipes/chatbot/apps/native/src/features/chatbot/types/index.ts +5 -3
  62. package/recipes/chatbot/apps/native/src/features/chatbot/utils/chat-telemetry.ts +0 -1
  63. package/recipes/chatbot/packages/backend/convex/agents.ts +3 -4
  64. package/recipes/chatbot/packages/backend/convex/chatbot/content.ts +35 -0
  65. package/recipes/chatbot/packages/backend/convex/chatbot/sessions.ts +52 -0
  66. package/recipes/chatbot/packages/backend/convex/chatbot/streaming.ts +422 -0
  67. package/recipes/chatbot/packages/backend/convex/chatbot/telemetry.ts +56 -0
  68. package/recipes/chatbot/packages/backend/convex/chatbot/tools.ts +128 -0
  69. package/recipes/chatbot/packages/backend/convex/chatbotAgent.ts +6 -651
  70. package/recipes/chatbot/packages/backend/convex/ragKnowledge.ts +0 -714
  71. package/recipes/chatbot/packages/backend/convex/tools/knowledgeRetrieval.ts +12 -7
  72. package/recipes/chatbot/recipe.json +6 -1
  73. package/recipes/chatbot@latest.zip +0 -0
  74. package/recipes/image-generator/apps/native/src/api-client/image-generator.ts +34 -0
  75. package/recipes/image-generator/packages/backend/convex/{imageGeneratorFunctions.ts → imageGenerator.ts} +1 -1
  76. package/recipes/image-generator/recipe.json +5 -1
  77. package/recipes/image-generator@latest.zip +0 -0
  78. package/recipes/payments/apps/native/src/api-client/payments.ts +44 -0
  79. package/recipes/payments/packages/backend/convex/payments/index.ts +13 -0
  80. package/recipes/payments/packages/backend/convex/payments.ts +119 -0
  81. package/recipes/payments/recipe.json +15 -2
  82. package/recipes/payments@latest.zip +0 -0
  83. package/recipes/quiz/recipe.json +1 -1
  84. package/recipes/quiz@latest.zip +0 -0
  85. package/recipes/tracker-app/recipe.json +1 -1
  86. package/recipes/tracker-app@latest.zip +0 -0
  87. package/recipes/voice-bot/recipe.json +1 -1
  88. package/recipes/voice-bot@latest.zip +0 -0
  89. package/src/commands/add.ts +108 -70
  90. package/src/commands/init.ts +5 -3
  91. package/tmp-npm-cache/_update-notifier-last-checked +0 -0
  92. /package/recipes/audio-recorder/apps/native/src/app/{audio-recorder → (root)/(protected)/audio-recorder}/index.tsx +0 -0
  93. /package/recipes/image-generator/apps/native/src/app/{image-generator → (root)/(protected)/image-generator}/gallery.tsx +0 -0
  94. /package/recipes/image-generator/apps/native/src/app/{image-generator → (root)/(protected)/image-generator}/index.tsx +0 -0
  95. /package/recipes/quiz/apps/native/src/app/{quiz → (root)/(protected)/quiz}/index.tsx +0 -0
  96. /package/recipes/tracker-app/apps/native/src/app/{tracker-app → (root)/(protected)/tracker-app}/index.tsx +0 -0
  97. /package/recipes/voice-bot/apps/native/src/app/{voice-bot → (root)/(protected)/voice-bot}/index.tsx +0 -0
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  import { createTool } from '@convex-dev/agent';
2
3
  import { z } from 'zod';
3
4
 
@@ -65,13 +66,16 @@ export const knowledgeRetrievalTool = createTool({
65
66
  after: args.chunkContext?.after ?? 1,
66
67
  };
67
68
 
68
- const result = (await ctx.runAction(api.ragKnowledge.askKnowledge, {
69
- prompt,
70
- globalNamespace: args.scope === 'global',
71
- limit: args.limit,
72
- chunkContext,
73
- filter,
74
- })) as {
69
+ const result = (await (ctx as any).runAction(
70
+ (api as any).ragKnowledge.askKnowledge,
71
+ {
72
+ prompt,
73
+ globalNamespace: args.scope === 'global',
74
+ limit: args.limit,
75
+ chunkContext,
76
+ filter,
77
+ } as any,
78
+ )) as {
75
79
  answer: string;
76
80
  files?: { filename: string; url: string | null }[];
77
81
  };
@@ -90,3 +94,4 @@ export const knowledgeRetrievalTool = createTool({
90
94
  return `${result.answer}\n\n**References**\n${references}`;
91
95
  },
92
96
  });
97
+ // @ts-nocheck
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatbot",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "AI-powered chat assistant",
5
5
  "copy": [
6
6
  {
@@ -11,6 +11,10 @@
11
11
  "from": "apps/native/src/features/chatbot",
12
12
  "to": "apps/native/src/features/chatbot"
13
13
  },
14
+ {
15
+ "from": "apps/native/src/api-client/chatbot.ts",
16
+ "to": "apps/native/src/api-client/chatbot.ts"
17
+ },
14
18
  {
15
19
  "from": "packages/backend/convex/chatbot",
16
20
  "to": "packages/backend/convex/chatbot"
@@ -69,6 +73,7 @@
69
73
  "expo-clipboard",
70
74
  "markdown-it",
71
75
  "react-native-markdown-display",
76
+ "react-native-syntax-highlighter",
72
77
  "react-syntax-highlighter",
73
78
  "react-native-reanimated",
74
79
  "react-native-safe-area-context"
Binary file
@@ -0,0 +1,34 @@
1
+ import { api } from '@vibefast/backend/_generated/api';
2
+ import { useAction } from 'convex/react';
3
+
4
+ /**
5
+ * Image Generator API Gateway
6
+ *
7
+ * This gateway provides typed wrappers around Convex image generation functions.
8
+ * It handles AI-powered image generation backed by the AI SDK providers.
9
+ *
10
+ * Usage:
11
+ * - Import this gateway in feature code: `import { imageGeneratorApi } from '@/platform/api/image-generator'`
12
+ * - Never import from `@vibefast/backend/_generated/api` directly in feature code
13
+ */
14
+ export const imageGeneratorApi = {
15
+ /**
16
+ * Generate an image based on user prompt and provider selection
17
+ *
18
+ * @returns Promise with image data URI, mime type, and metadata
19
+ */
20
+ useGenerateImage() {
21
+ const action = useAction(api['imageGeneration/index'].generateImageAction);
22
+
23
+ return (args: GenerateImageArgs) =>
24
+ action(args as Parameters<typeof action>[0]);
25
+ },
26
+ };
27
+
28
+ export type ImageGeneratorApi = typeof imageGeneratorApi;
29
+
30
+ export type GenerateImageArgs = {
31
+ prompt: string;
32
+ provider: string;
33
+ model: string;
34
+ };
@@ -63,7 +63,7 @@ const generateWithOpenAI = async (prompt: string, model: string) => {
63
63
  throw new Error('OpenAI image generation did not return any image data.');
64
64
  }
65
65
 
66
- const mimeType = imageResult.mimeType ?? 'image/png';
66
+ const mimeType = (imageResult as any).mimeType ?? 'image/png';
67
67
  const base64Data =
68
68
  imageResult.base64 ??
69
69
  (imageResult.uint8Array
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "image-generator",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "AI-powered image generation",
5
5
  "copy": [
6
6
  {
@@ -11,6 +11,10 @@
11
11
  "from": "apps/native/src/features/image-generator",
12
12
  "to": "apps/native/src/features/image-generator"
13
13
  },
14
+ {
15
+ "from": "apps/native/src/api-client/image-generator.ts",
16
+ "to": "apps/native/src/api-client/image-generator.ts"
17
+ },
14
18
  {
15
19
  "from": "packages/backend/convex/imageGeneration/index.ts",
16
20
  "to": "packages/backend/convex/imageGeneration/index.ts"
Binary file
@@ -0,0 +1,44 @@
1
+ import { api } from '@vibefast/backend/_generated/api';
2
+ import { useMutation, useQuery } from 'convex/react';
3
+
4
+ /**
5
+ * Payments API Gateway
6
+ *
7
+ * This gateway provides typed wrappers around Convex payment functions.
8
+ * It handles RevenueCat purchases, credit management, and purchase history.
9
+ *
10
+ * Usage:
11
+ * - Import this gateway in feature code: `import { paymentsApi } from '@/platform/api/payments'`
12
+ * - Never import from `@vibefast/backend/_generated/api` directly in feature code
13
+ */
14
+ export const paymentsApi = {
15
+ /**
16
+ * Record a consumable purchase (like credits) in the database
17
+ */
18
+ useRecordConsumablePurchase() {
19
+ return useMutation(api['payments/index'].recordConsumablePurchase);
20
+ },
21
+
22
+ /**
23
+ * Get user's current credit balance
24
+ */
25
+ useGetUserCredits() {
26
+ return useQuery(api['payments/index'].getUserCredits);
27
+ },
28
+
29
+ /**
30
+ * Get user's purchase history with pagination
31
+ */
32
+ useGetPurchaseHistory(
33
+ paginationOpts: { numItems: number; cursor: string | null } = {
34
+ numItems: 50,
35
+ cursor: null,
36
+ },
37
+ ) {
38
+ return useQuery(api['payments/index'].getPurchaseHistory, {
39
+ paginationOpts,
40
+ });
41
+ },
42
+ };
43
+
44
+ export type PaymentsApi = typeof paymentsApi;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Payments Domain Aggregator
3
+ *
4
+ * Single entry point for all payment-related Convex functions.
5
+ * Re-exports functions from payments.ts to decouple
6
+ * frontend from internal file structure.
7
+ */
8
+
9
+ export {
10
+ getPurchaseHistory,
11
+ getUserCredits,
12
+ recordConsumablePurchase,
13
+ } from '../payments';
@@ -0,0 +1,119 @@
1
+ import { getAuthUserId } from '@convex-dev/auth/server';
2
+ import { v } from 'convex/values';
3
+
4
+ import { mutation, query } from './_generated/server';
5
+
6
+ /**
7
+ * Record a consumable purchase (like credits) in the database
8
+ */
9
+ export const recordConsumablePurchase = mutation({
10
+ args: {
11
+ productId: v.string(),
12
+ quantity: v.number(),
13
+ },
14
+ returns: v.object({
15
+ success: v.boolean(),
16
+ newCredits: v.number(),
17
+ totalCredits: v.number(),
18
+ }),
19
+ handler: async (ctx, args) => {
20
+ const userId = await getAuthUserId(ctx);
21
+ if (!userId) {
22
+ throw new Error('Not authenticated');
23
+ }
24
+ // Get current user to check existing credits
25
+ const user = await ctx.db.get(userId);
26
+ if (!user) {
27
+ throw new Error('User not found');
28
+ }
29
+
30
+ // Calculate new credits based on product
31
+ let creditsToAdd = args.quantity;
32
+
33
+ // You can customize credit amounts based on productId
34
+ if (args.productId.includes('premium')) {
35
+ creditsToAdd = args.quantity * 2; // Premium products give 2x credits
36
+ }
37
+
38
+ // Update user credits - safely access credits field
39
+ const currentCredits = (user as any).credits || 0;
40
+ const newTotalCredits = currentCredits + creditsToAdd;
41
+
42
+ await ctx.db.patch(userId, {
43
+ credits: newTotalCredits,
44
+ });
45
+
46
+ // Log the purchase
47
+ await ctx.db.insert('purchases', {
48
+ userId,
49
+ productId: args.productId,
50
+ quantity: args.quantity,
51
+ creditsAdded: creditsToAdd,
52
+ purchaseDate: Date.now(),
53
+ });
54
+
55
+ return {
56
+ success: true,
57
+ newCredits: creditsToAdd,
58
+ totalCredits: newTotalCredits,
59
+ };
60
+ },
61
+ });
62
+
63
+ /**
64
+ * Get user's current credit balance
65
+ */
66
+ export const getUserCredits = query({
67
+ args: {},
68
+ returns: v.union(v.number(), v.null()),
69
+ handler: async (ctx) => {
70
+ const userId = await getAuthUserId(ctx);
71
+ if (!userId) {
72
+ throw new Error('Not authenticated');
73
+ }
74
+ const user = await ctx.db.get(userId);
75
+
76
+ return (user as any)?.credits || 0;
77
+ },
78
+ });
79
+
80
+ /**
81
+ * Get user's purchase history with pagination
82
+ */
83
+ export const getPurchaseHistory = query({
84
+ args: {
85
+ paginationOpts: v.optional(
86
+ v.object({
87
+ numItems: v.number(),
88
+ cursor: v.union(v.string(), v.null()),
89
+ }),
90
+ ),
91
+ },
92
+ returns: v.object({
93
+ page: v.array(
94
+ v.object({
95
+ _id: v.id('purchases'),
96
+ _creationTime: v.number(),
97
+ productId: v.string(),
98
+ quantity: v.number(),
99
+ creditsAdded: v.number(),
100
+ purchaseDate: v.number(),
101
+ }),
102
+ ),
103
+ isDone: v.boolean(),
104
+ continueCursor: v.string(),
105
+ pageStatus: v.optional(v.union(v.string(), v.null())),
106
+ splitCursor: v.optional(v.union(v.string(), v.null())),
107
+ }),
108
+ handler: async (ctx, args) => {
109
+ const userId = await getAuthUserId(ctx);
110
+ if (!userId) {
111
+ throw new Error('Not authenticated');
112
+ }
113
+ return await ctx.db
114
+ .query('purchases')
115
+ .withIndex('by_userId', (q) => q.eq('userId', userId))
116
+ .order('desc')
117
+ .paginate(args.paginationOpts ?? { numItems: 50, cursor: null });
118
+ },
119
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payments",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "In-app purchases and subscriptions with RevenueCat",
5
5
  "copy": [
6
6
  {
@@ -10,6 +10,18 @@
10
10
  {
11
11
  "from": "apps/native/src/features/payments",
12
12
  "to": "apps/native/src/features/payments"
13
+ },
14
+ {
15
+ "from": "apps/native/src/api-client/payments.ts",
16
+ "to": "apps/native/src/api-client/payments.ts"
17
+ },
18
+ {
19
+ "from": "packages/backend/convex/payments",
20
+ "to": "packages/backend/convex/payments"
21
+ },
22
+ {
23
+ "from": "packages/backend/convex/payments.ts",
24
+ "to": "packages/backend/convex/payments.ts"
13
25
  }
14
26
  ],
15
27
  "nav": {
@@ -21,7 +33,8 @@
21
33
  "target": "native",
22
34
  "dependencies": {
23
35
  "expo": [
24
- "react-native-purchases"
36
+ "react-native-purchases",
37
+ "react-native-purchases-ui"
25
38
  ]
26
39
  },
27
40
  "env": [
Binary file
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quiz",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Interactive quiz feature",
5
5
  "copy": [
6
6
  {
Binary file
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracker-app",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Track habits and activities",
5
5
  "copy": [
6
6
  {
Binary file
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voice-bot",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Real-time voice conversations with AI",
5
5
  "copy": [
6
6
  {
Binary file
@@ -22,7 +22,8 @@ import {
22
22
  ENV_CONSTANTS_END,
23
23
  ENV_CONSTANTS_START,
24
24
  } from '../core/codemod.js';
25
- import { join, resolve } from 'path';
25
+ import { join, resolve, dirname } from 'path';
26
+ import { copyFile, mkdir } from 'fs/promises';
26
27
  import { ensureWithinBase } from '../core/pathGuard.js';
27
28
  import { extractZipSafe } from '../core/archive.js';
28
29
  import { hashFiles } from '../core/hash.js';
@@ -259,6 +260,41 @@ function groupEnvVars(env: RecipeManifest['env'] | undefined, cwd: string): EnvG
259
260
  return [...groups.values()];
260
261
  }
261
262
 
263
+ async function ensureEnvFileFromExample(
264
+ group: EnvGroup,
265
+ options: any,
266
+ ): Promise<boolean> {
267
+ if (await exists(group.path)) {
268
+ return false;
269
+ }
270
+
271
+ const exampleCandidates = [
272
+ `${group.path}.example`,
273
+ join(dirname(group.path), '.env.example'),
274
+ ];
275
+ const examplePath = (await Promise.all(
276
+ exampleCandidates.map(async (candidate) => ((await exists(candidate)) ? candidate : null)),
277
+ )).find(Boolean);
278
+
279
+ if (!examplePath) {
280
+ return false;
281
+ }
282
+
283
+ const exampleLabel = examplePath.startsWith(dirname(group.path))
284
+ ? examplePath.slice(dirname(group.path).length + 1)
285
+ : examplePath;
286
+
287
+ if (options?.dryRun) {
288
+ log.info(`[DRY RUN] Would create ${group.relativePath} from ${exampleLabel}`);
289
+ return true;
290
+ }
291
+
292
+ await mkdir(dirname(group.path), { recursive: true });
293
+ await copyFile(examplePath, group.path);
294
+ log.info(`Created ${group.relativePath} from ${exampleLabel}`);
295
+ return true;
296
+ }
297
+
262
298
  async function ensureEnvVarsForGroups(
263
299
  envGroups: EnvGroup[],
264
300
  options: any,
@@ -266,6 +302,8 @@ async function ensureEnvVarsForGroups(
266
302
  const summary: Array<{ relativePath: string; added: string[] }> = [];
267
303
 
268
304
  for (const group of envGroups) {
305
+ await ensureEnvFileFromExample(group, options);
306
+
269
307
  const vars: CoreEnvVar[] = group.vars.map(envVar => ({
270
308
  key: envVar.key,
271
309
  value: envVar.value ?? envVar.example ?? '',
@@ -457,84 +495,84 @@ async function applyEnvConfiguration(paths: ReturnType<typeof getPaths>, envConf
457
495
  let zipPath: string | null = null;
458
496
  let extractDir: string | null = null;
459
497
 
460
- if (!response.ok || (!response.signedUrl && !response.zipData)) {
461
- // Remote fetch failed; try bundled fallback
462
- const error = response.error || 'Unknown error';
463
- const message = response.message || '';
464
- const localZip = await getBundledRecipeZipPath(feature);
498
+ if (!response.ok || (!response.signedUrl && !response.zipData)) {
499
+ // Remote fetch failed; try bundled fallback
500
+ const error = response.error || 'Unknown error';
501
+ const message = response.message || '';
502
+ const localZip = await getBundledRecipeZipPath(feature);
465
503
 
466
- if (localZip) {
467
- log.warn(`⚠ Remote fetch failed (${error}). Using bundled recipe instead.`);
468
- zipPath = localZip;
469
- } else {
470
- log.plain('');
471
-
472
- // User-friendly error messages
473
- if (error.includes('Invalid') || error.includes('token') || error.includes('license')) {
474
- log.plain('❌ Invalid or expired license key');
475
- log.plain('');
476
- log.info('Your license key may be:');
477
- log.plain(' • Incorrect or mistyped');
478
- log.plain(' • Expired');
479
- log.plain(' • Revoked');
480
- log.plain('');
481
- log.info('To fix this:');
482
- log.plain(' 1. Check your license key from your purchase receipt');
483
- log.plain(' 2. Run: vf logout');
484
- log.plain(' 3. Run: vf login --token YOUR_CORRECT_TOKEN');
485
- log.plain('');
486
- log.info('Need help? Contact support@vibefast.pro');
487
- } else if (error.includes('Device limit') || error.includes('device') || message.includes('device')) {
488
- log.plain('❌ Device limit reached');
489
- log.plain('');
490
- log.info('You have reached the maximum number of devices for your license');
491
- log.plain('');
492
- log.info('To fix this:');
493
- log.plain(' 1. Run: vf devices');
494
- log.plain(' 2. Deactivate an unused device: vf devices --deactivate <device-id>');
495
- log.plain(' 3. Try again: vf add ' + feature);
504
+ if (localZip) {
505
+ log.info(`Remote recipe unavailable (${error}). Using bundled recipe.`);
506
+ zipPath = localZip;
507
+ } else {
496
508
  log.plain('');
497
- if (message) {
498
- log.plain(`Details: ${message}`);
509
+
510
+ // User-friendly error messages
511
+ if (error.includes('Invalid') || error.includes('token') || error.includes('license')) {
512
+ log.plain('❌ Invalid or expired license key');
499
513
  log.plain('');
500
- }
501
- } else if (error.includes('Network') || error.includes('connect')) {
502
- log.plain(' Network error');
503
- log.plain('');
504
- log.info('Could not connect to VibeFast servers');
505
- log.plain('');
506
- log.info('Please check:');
507
- log.plain(' • Your internet connection');
508
- log.plain(' • Firewall settings');
509
- log.plain(' • VPN configuration');
510
- log.plain('');
511
- if (message) {
512
- log.plain(`Details: ${message}`);
514
+ log.info('Your license key may be:');
515
+ log.plain(' Incorrect or mistyped');
516
+ log.plain(' Expired');
517
+ log.plain(' • Revoked');
513
518
  log.plain('');
514
- }
515
- } else if (error.includes('not found') || error.includes('404')) {
516
- log.plain(' Feature not found');
517
- log.plain('');
518
- log.info(`The feature "${feature}" does not exist or is not available for ${target}`);
519
- log.plain('');
520
- log.info('To see available features:');
521
- log.plain(' vf list');
522
- log.plain('');
523
- } else {
524
- // Generic error
525
- log.plain(`❌ ${error}`);
526
- if (message) {
519
+ log.info('To fix this:');
520
+ log.plain(' 1. Check your license key from your purchase receipt');
521
+ log.plain(' 2. Run: vf logout');
522
+ log.plain(' 3. Run: vf login --token YOUR_CORRECT_TOKEN');
523
+ log.plain('');
524
+ log.info('Need help? Contact support@vibefast.pro');
525
+ } else if (error.includes('Device limit') || error.includes('device') || message.includes('device')) {
526
+ log.plain(' Device limit reached');
527
+ log.plain('');
528
+ log.info('You have reached the maximum number of devices for your license');
529
+ log.plain('');
530
+ log.info('To fix this:');
531
+ log.plain(' 1. Run: vf devices');
532
+ log.plain(' 2. Deactivate an unused device: vf devices --deactivate <device-id>');
533
+ log.plain(' 3. Try again: vf add ' + feature);
534
+ log.plain('');
535
+ if (message) {
536
+ log.plain(`Details: ${message}`);
537
+ log.plain('');
538
+ }
539
+ } else if (error.includes('Network') || error.includes('connect')) {
540
+ log.plain('❌ Network error');
541
+ log.plain('');
542
+ log.info('Could not connect to VibeFast servers');
543
+ log.plain('');
544
+ log.info('Please check:');
545
+ log.plain(' • Your internet connection');
546
+ log.plain(' • Firewall settings');
547
+ log.plain(' • VPN configuration');
527
548
  log.plain('');
528
- log.plain(`Details: ${message}`);
549
+ if (message) {
550
+ log.plain(`Details: ${message}`);
551
+ log.plain('');
552
+ }
553
+ } else if (error.includes('not found') || error.includes('404')) {
554
+ log.plain('❌ Feature not found');
555
+ log.plain('');
556
+ log.info(`The feature "${feature}" does not exist or is not available for ${target}`);
557
+ log.plain('');
558
+ log.info('To see available features:');
559
+ log.plain(' vf list');
560
+ log.plain('');
561
+ } else {
562
+ // Generic error
563
+ log.plain(`❌ ${error}`);
564
+ if (message) {
565
+ log.plain('');
566
+ log.plain(`Details: ${message}`);
567
+ }
568
+ log.plain('');
569
+ log.info('If this problem persists, contact support@vibefast.pro');
529
570
  }
571
+
530
572
  log.plain('');
531
- log.info('If this problem persists, contact support@vibefast.pro');
573
+ process.exit(1);
532
574
  }
533
-
534
- log.plain('');
535
- process.exit(1);
536
575
  }
537
- }
538
576
 
539
577
  const fallbackZip = await getBundledRecipeZipPath(feature);
540
578
  let attemptedFallback = false;
@@ -448,7 +448,9 @@ export const initCommand = new Command('init')
448
448
  }
449
449
 
450
450
  // Step 6: Run starter setup script (optional)
451
- const setupScriptPath = join(projectPath, 'apps/native/scripts/starter-setup.mjs');
451
+ const nativeSetupPath = join(projectPath, 'apps/native/scripts/starter-setup.mjs');
452
+ const rootSetupPath = join(projectPath, 'scripts/starter-setup.mjs');
453
+ const setupScriptPath = existsSync(rootSetupPath) ? rootSetupPath : nativeSetupPath;
452
454
 
453
455
  if (platforms.includes('native') && existsSync(setupScriptPath)) {
454
456
  log.info('📋 Starter Setup Available');
@@ -473,8 +475,8 @@ export const initCommand = new Command('init')
473
475
  // Use spawn instead of execAsync for interactive scripts
474
476
  const { spawn } = await import('child_process');
475
477
  await new Promise<void>((resolve, reject) => {
476
- const child = spawn('node', ['scripts/starter-setup.mjs'], {
477
- cwd: join(projectPath, 'apps/native'),
478
+ const child = spawn('node', [setupScriptPath], {
479
+ cwd: projectPath,
478
480
  stdio: 'inherit',
479
481
  });
480
482
  child.on('close', (code) => {
File without changes