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 +1 -1
- package/scripts/create-snappy/cli.js +104 -34
package/package.json
CHANGED
|
@@ -155,11 +155,7 @@ async function githubLogin() {
|
|
|
155
155
|
// --- Main CLI ---
|
|
156
156
|
|
|
157
157
|
function getPackageManager() {
|
|
158
|
-
|
|
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: '
|
|
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 || '
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
//
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
)
|
|
381
|
-
|
|
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 {
|