create-snappy 1.1.0 → 1.1.2

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.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "The official installer for the SNAPPY stack.",
5
5
  "main": "scripts/create-snappy/cli.js",
6
6
  "bin": {
@@ -155,11 +155,7 @@ async function githubLogin() {
155
155
  // --- Main CLI ---
156
156
 
157
157
  function getPackageManager() {
158
- const userAgent = process.env.npm_config_user_agent || ''
159
- if (userAgent.startsWith('yarn')) return 'yarn'
160
- if (userAgent.startsWith('pnpm')) return 'pnpm'
161
- if (userAgent.startsWith('bun')) return 'bun'
162
- return 'npm'
158
+ return 'pnpm'
163
159
  }
164
160
 
165
161
  async function main() {
@@ -229,12 +225,19 @@ async function main() {
229
225
  name: 'template',
230
226
  message: 'Which template would you like to use?',
231
227
  choices: [
232
- { title: 'Portfolio', description: 'A sleek portfolio template', value: 'portofolio' },
228
+ { title: 'Portfolio', description: 'A sleek portfolio template', value: 'portfolio' },
233
229
  ],
234
230
  initial: 0,
235
231
  })
236
232
  }
237
233
 
234
+ questions.push({
235
+ type: 'password',
236
+ name: 'snappyLicenseKey',
237
+ message: 'SNAPPY License Key (leave blank for trial)?',
238
+ initial: savedConfig.snappyLicenseKey || '',
239
+ })
240
+
238
241
  // Guided Setup Questions
239
242
  const needsGuided = options.guided || !hasSavedConfig
240
243
 
@@ -305,6 +308,7 @@ async function main() {
305
308
  const config = {
306
309
  ...savedConfig,
307
310
  authorName: response.authorName || savedConfig.authorName,
311
+ snappyLicenseKey: response.snappyLicenseKey || savedConfig.snappyLicenseKey,
308
312
  supabaseUrl: response.supabaseUrl || savedConfig.supabaseUrl,
309
313
  supabaseAnonKey: response.supabaseAnonKey || savedConfig.supabaseAnonKey,
310
314
  supabaseServiceRole: response.supabaseServiceRole || savedConfig.supabaseServiceRole,
@@ -323,7 +327,7 @@ async function main() {
323
327
  const projectName = providedName || response.projectName
324
328
  const projectDescription = response.projectDescription
325
329
  const authorName = config.authorName
326
- const selectedTemplate = options.template || response.template || 'portofolio'
330
+ const selectedTemplate = options.template || response.template || 'portfolio'
327
331
 
328
332
  const targetDir = path.resolve(process.cwd(), projectName)
329
333
 
@@ -345,41 +349,92 @@ async function main() {
345
349
  // Create target dir
346
350
  fs.mkdirSync(targetDir, { recursive: true })
347
351
 
348
- try {
349
- // 1. Initialize from repository
350
- const token = getSavedToken() || (await githubLogin())
351
-
352
- const repoUrl = process.env.SNAPPY_REPO_URL || 'https://github.com/snappy-stack/snappy'
353
- let authenticatedUrl = repoUrl
352
+ let finalLicenseToken = config.snappyLicenseKey;
353
+ let isTrial = false;
354
+ let machineIdToSave = null;
355
+ let skippedLicense = false;
354
356
 
355
- if (token) {
356
- authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${token}@`)
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;
357
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
+ }
358
398
 
399
+ try {
400
+ // 1. Initialize from repository
401
+ const repoUrl = process.env.SNAPPY_REPO_URL || 'https://github.com/snappy-stack/snappy'
359
402
  const cloneSpinner = ora(`Cloning secure template (${selectedTemplate})...`).start()
403
+
360
404
  try {
361
- // Use branch as the template name
362
- execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
363
- stdio: 'ignore',
364
- })
365
- cloneSpinner.succeed('Project cloned successfully.')
366
- } catch (cloneErr) {
367
- cloneSpinner.stop()
368
- if (!token) {
369
- console.log(pc.yellow('\nšŸ” This template might be private. Attempting login...'))
370
- token = await githubLogin()
371
- authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${token}@`)
372
- const retrySpinner = ora(`Cloning secure template (${selectedTemplate})...`).start()
373
- execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
405
+ // Try public clone first for public repo
406
+ try {
407
+ execSync(`git clone --depth 1 -b ${selectedTemplate} ${repoUrl} "${targetDir}"`, {
374
408
  stdio: 'ignore',
375
409
  })
376
- retrySpinner.succeed('Project cloned successfully.')
377
- } else {
378
- console.error(
379
- pc.red("Clone failed. The branch might not exist, or you don't have access."),
380
- )
381
- throw cloneErr
410
+ cloneSpinner.succeed('Project cloned successfully (public).')
411
+ } catch (publicErr) {
412
+ // If public fails, try with token
413
+ const token = getSavedToken()
414
+ if (token) {
415
+ const authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${token}@`)
416
+ execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
417
+ stdio: 'ignore',
418
+ })
419
+ cloneSpinner.succeed('Project cloned successfully (authenticated).')
420
+ } else {
421
+ // No token, ask for one
422
+ cloneSpinner.stop()
423
+ console.log(pc.yellow('\nšŸ” This template might be private. Please authenticate...'))
424
+ const newToken = await githubLogin()
425
+ const authenticatedUrl = repoUrl.replace('https://', `https://x-access-token:${newToken}@`)
426
+ const retrySpinner = ora(`Cloning secure template (${selectedTemplate})...`).start()
427
+ execSync(`git clone --depth 1 -b ${selectedTemplate} ${authenticatedUrl} "${targetDir}"`, {
428
+ stdio: 'ignore',
429
+ })
430
+ retrySpinner.succeed('Project cloned successfully.')
431
+ }
382
432
  }
433
+ } catch (err) {
434
+ cloneSpinner.stop()
435
+ console.error(pc.red("\nāŒ Clone failed. The branch might not exist, or you don't have access."))
436
+ console.error(pc.gray(`Command: git clone -b ${selectedTemplate} ${repoUrl}`))
437
+ throw err
383
438
  }
384
439
 
385
440
  if (fs.existsSync(path.join(targetDir, '.git'))) {
@@ -412,6 +467,7 @@ async function main() {
412
467
  NEXT_PUBLIC_SUPABASE_ANON_KEY="${config.supabaseAnonKey}"
413
468
  SUPABASE_SERVICE_ROLE_KEY="${config.supabaseServiceRole}"
414
469
  SUPABASE_URL="${config.supabaseUrl}"
470
+ SNAPPY_LICENSE_TOKEN="${finalLicenseToken || ''}"
415
471
 
416
472
  # Database Configuration
417
473
  POSTGRES_URL="postgres://postgres:${config.supabaseServiceRole}@${config.supabaseUrl.replace('https://', '').split('.')[0]}.supabase.co:5432/postgres"
@@ -447,6 +503,20 @@ REQUIRE_LOGIN="yes"
447
503
 
448
504
  console.log(pc.green('\nāœ… SNAPPY Stack is perfectly prepared and ready to launch!'))
449
505
  console.log(`\nNext steps:\n cd ${pc.bold(projectName)}\n ${pmRun}\n`)
506
+
507
+ if (skippedLicense) {
508
+ console.log(pc.red('\n ──────────────────────────────────'));
509
+ console.log(` šŸ”‘ ${pc.bold(pc.white('Add your license key to .env'))}`);
510
+ console.log(pc.gray(' SNAPPY_LICENSE_TOKEN=sk_snappy_...'));
511
+ console.log('');
512
+ console.log(` šŸ“” ${pc.cyan('Get your key: wicky.id')}`);
513
+ console.log(` šŸ“– ${pc.cyan('Docs: snappycore.wicky.id')}`);
514
+ console.log(pc.red(' ──────────────────────────────────'));
515
+ console.log(pc.bold(pc.red('\n THERE IS NO MERCY IN PRODUCTION šŸ‘¹ \n')));
516
+ } else if (isTrial) {
517
+ console.log(pc.green('\nāœ… 2-hour free trial active! Your trial license has been added to .env.'));
518
+ }
519
+
450
520
  } catch (err) {
451
521
  console.error(pc.red(`Installation failed: ${err.message || err}`))
452
522
  } finally {