create-snappy 1.0.0 → 1.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-snappy",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "The official installer for the SNAPPY stack.",
5
5
  "main": "scripts/create-snappy/cli.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "commander": "^14.0.3",
15
+ "ora": "^5.4.1",
15
16
  "picocolors": "^1.1.1",
16
17
  "prompts": "^2.4.2"
17
18
  },
@@ -14,6 +14,16 @@ import os from 'os'
14
14
  import { Command } from 'commander'
15
15
  import prompts from 'prompts'
16
16
  import pc from 'picocolors'
17
+ import ora from 'ora'
18
+
19
+ const SNAPPY_LOGO = `
20
+ ███████╗███╗ ██╗ █████╗ ██████╗ ██████╗ ██╗ ██╗
21
+ ██╔════╝████╗ ██║██╔══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝
22
+ ███████╗██╔██╗ ██║███████║██████╔╝██████╔╝ ╚████╔╝
23
+ ╚════██║██║╚██╗██║██╔══██║██╔═══╝ ██╔═══╝ ╚██╔╝
24
+ ███████║██║ ╚████║██║ ██║██║ ██║ ██║
25
+ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
26
+ `
17
27
 
18
28
  const __filename = fileURLToPath(import.meta.url)
19
29
  const __dirname = path.dirname(__filename)
@@ -144,8 +154,13 @@ async function githubLogin() {
144
154
 
145
155
  // --- Main CLI ---
146
156
 
157
+ function getPackageManager() {
158
+ return 'pnpm'
159
+ }
160
+
147
161
  async function main() {
148
- console.log(pc.cyan('\n🚀 Welcome to the SNAPPY Stack Installer!'))
162
+ console.log(pc.cyan(SNAPPY_LOGO))
163
+ console.log(pc.cyan('🚀 Welcome to the SNAPPY Stack Installer!'))
149
164
  console.log('------------------------------------------')
150
165
 
151
166
  const program = new Command()
@@ -216,6 +231,13 @@ async function main() {
216
231
  })
217
232
  }
218
233
 
234
+ questions.push({
235
+ type: 'password',
236
+ name: 'snappyLicenseKey',
237
+ message: 'SNAPPY License Key (leave blank to skip)?',
238
+ initial: savedConfig.snappyLicenseKey || '',
239
+ })
240
+
219
241
  // Guided Setup Questions
220
242
  const needsGuided = options.guided || !hasSavedConfig
221
243
 
@@ -286,6 +308,7 @@ async function main() {
286
308
  const config = {
287
309
  ...savedConfig,
288
310
  authorName: response.authorName || savedConfig.authorName,
311
+ snappyLicenseKey: response.snappyLicenseKey || savedConfig.snappyLicenseKey,
289
312
  supabaseUrl: response.supabaseUrl || savedConfig.supabaseUrl,
290
313
  supabaseAnonKey: response.supabaseAnonKey || savedConfig.supabaseAnonKey,
291
314
  supabaseServiceRole: response.supabaseServiceRole || savedConfig.supabaseServiceRole,
@@ -326,6 +349,53 @@ async function main() {
326
349
  // Create target dir
327
350
  fs.mkdirSync(targetDir, { recursive: true })
328
351
 
352
+ let finalLicenseToken = config.snappyLicenseKey;
353
+ let isTrial = false;
354
+ let machineIdToSave = null;
355
+ let skippedLicense = false;
356
+
357
+ if (!finalLicenseToken || finalLicenseToken.trim() === '') {
358
+ const trialResponse = await prompts({
359
+ type: 'confirm',
360
+ name: 'startTrial',
361
+ message: '🎯 Start 2-hour free trial?',
362
+ initial: true
363
+ });
364
+
365
+ if (trialResponse.startTrial) {
366
+ console.log(pc.cyan('\n⏳ Setting up 2-hour free trial...'));
367
+ const machineId = Math.random().toString(36).substring(2) + Date.now().toString(36);
368
+
369
+ try {
370
+ const res = await fetch('https://snappycore.wicky.id/api/trial', {
371
+ method: 'POST',
372
+ headers: { 'Content-Type': 'application/json' },
373
+ body: JSON.stringify({ machineId })
374
+ });
375
+
376
+ const data = await res.json();
377
+ if (res.ok && data.token) {
378
+ finalLicenseToken = data.token;
379
+ isTrial = true;
380
+ machineIdToSave = machineId;
381
+ } else {
382
+ console.warn(pc.yellow(`⚠️ Trial generation failed: ${data.error || 'Unknown error'}. Continuing without license.`));
383
+ skippedLicense = true;
384
+ }
385
+ } catch (err) {
386
+ console.warn(pc.yellow(`⚠️ Could not reach licensing server: ${err.message}`));
387
+ skippedLicense = true;
388
+ }
389
+ } else {
390
+ skippedLicense = true;
391
+ }
392
+ }
393
+
394
+ // Save machine ID if it's a trial
395
+ if (machineIdToSave) {
396
+ fs.writeFileSync(path.join(targetDir, '.snappy-machine-id'), machineIdToSave);
397
+ }
398
+
329
399
  try {
330
400
  // 1. Initialize from repository
331
401
  const token = getSavedToken() || (await githubLogin())
@@ -337,20 +407,24 @@ async function main() {
337
407
  authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${token}@`)
338
408
  }
339
409
 
340
- console.log(`Cloning into ${pc.bold(projectName)}...`)
410
+ const cloneSpinner = ora(`Cloning secure template (${selectedTemplate})...`).start()
341
411
  try {
342
412
  // Use branch as the template name
343
413
  execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
344
- stdio: 'inherit',
414
+ stdio: 'ignore',
345
415
  })
416
+ cloneSpinner.succeed('Project cloned successfully.')
346
417
  } catch (cloneErr) {
418
+ cloneSpinner.stop()
347
419
  if (!token) {
348
420
  console.log(pc.yellow('\n🔐 This template might be private. Attempting login...'))
349
421
  token = await githubLogin()
350
422
  authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${token}@`)
423
+ const retrySpinner = ora(`Cloning secure template (${selectedTemplate})...`).start()
351
424
  execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
352
- stdio: 'inherit',
425
+ stdio: 'ignore',
353
426
  })
427
+ retrySpinner.succeed('Project cloned successfully.')
354
428
  } else {
355
429
  console.error(
356
430
  pc.red("Clone failed. The branch might not exist, or you don't have access."),
@@ -389,6 +463,7 @@ async function main() {
389
463
  NEXT_PUBLIC_SUPABASE_ANON_KEY="${config.supabaseAnonKey}"
390
464
  SUPABASE_SERVICE_ROLE_KEY="${config.supabaseServiceRole}"
391
465
  SUPABASE_URL="${config.supabaseUrl}"
466
+ SNAPPY_LICENSE_TOKEN="${finalLicenseToken || ''}"
392
467
 
393
468
  # Database Configuration
394
469
  POSTGRES_URL="postgres://postgres:${config.supabaseServiceRole}@${config.supabaseUrl.replace('https://', '').split('.')[0]}.supabase.co:5432/postgres"
@@ -405,19 +480,39 @@ PUBLIC_FRONTEND_URL="http://localhost:3000"
405
480
  REQUIRE_LOGIN="yes"
406
481
  `
407
482
  fs.writeFileSync(path.join(targetDir, '.env'), envContent)
408
- console.log(pc.green('✅ .env generated.'))
483
+ console.log(pc.green('✅ .env generated.\n'))
409
484
 
410
485
  if (process.env.SKIP_INSTALL !== 'true') {
411
- console.log(pc.magenta('\n📦 Installing dependencies (pnpm)...'))
486
+ const pm = getPackageManager()
487
+ const installCmd = pm === 'npm' ? 'npm install' : `${pm} install`
488
+ const installSpinner = ora(`Installing dependencies using ${pm}...`).start()
412
489
  try {
413
- execSync('pnpm install', { cwd: targetDir, stdio: 'inherit' })
490
+ execSync(installCmd, { cwd: targetDir, stdio: 'ignore' })
491
+ installSpinner.succeed('Dependencies installed successfully.')
414
492
  } catch (e) {
415
- console.warn(pc.yellow('Warning: pnpm install failed.'))
493
+ installSpinner.fail('Failed to install dependencies.')
494
+ console.warn(pc.yellow(`Warning: ${installCmd} failed.`))
416
495
  }
417
496
  }
418
497
 
419
- console.log(pc.green('\n✅ SNAPPY Stack is ready!'))
420
- console.log(`\nNext steps:\n cd ${pc.bold(projectName)}\n pnpm run dev\n`)
498
+ const pmRun = getPackageManager() === 'npm' ? 'npm run dev' : `${getPackageManager()} run dev`
499
+
500
+ console.log(pc.green('\n✅ SNAPPY Stack is perfectly prepared and ready to launch!'))
501
+ console.log(`\nNext steps:\n cd ${pc.bold(projectName)}\n ${pmRun}\n`)
502
+
503
+ if (skippedLicense) {
504
+ console.log(pc.red('\n ──────────────────────────────────'));
505
+ console.log(` 🔑 ${pc.bold(pc.white('Add your license key to .env'))}`);
506
+ console.log(pc.gray(' SNAPPY_LICENSE_TOKEN=sk_snappy_...'));
507
+ console.log('');
508
+ console.log(` 📡 ${pc.cyan('Get your key: wicky.id')}`);
509
+ console.log(` 📖 ${pc.cyan('Docs: snappycore.wicky.id')}`);
510
+ console.log(pc.red(' ──────────────────────────────────'));
511
+ console.log(pc.bold(pc.red('\n THERE IS NO MERCY IN PRODUCTION 👹 \n')));
512
+ } else if (isTrial) {
513
+ console.log(pc.green('\n✅ 2-hour free trial active! Your trial license has been added to .env.'));
514
+ }
515
+
421
516
  } catch (err) {
422
517
  console.error(pc.red(`Installation failed: ${err.message || err}`))
423
518
  } finally {