diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..ddc32ca --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,30 @@ +name: Publish to npm 🚀 + +on: + push: + branches: + - main + paths: + - package.json + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Install dependencies + run: npm ci + + - name: Publish to npm + run: npm publish --access public \ No newline at end of file diff --git a/bin/index.js b/bin/index.js index a2aaad8..5c3b053 100755 --- a/bin/index.js +++ b/bin/index.js @@ -6,6 +6,7 @@ import { init } from './init.js'; const args = process.argv.slice(2); let projectName = null; let autoAcceptDefaults = false; +let apiKey = null; // Check for -y flag const yIndex = args.indexOf('-y'); @@ -14,18 +15,48 @@ if (yIndex !== -1) { args.splice(yIndex, 1); // Remove -y from args } -// If there's a remaining argument, it's the project name -if (args.length > 0) { - projectName = args[0]; -} + // Parse other arguments + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg === '-p' || arg === '--project') { + if (i + 1 < args.length) { + projectName = args[i + 1]; + if (projectName.startsWith('-')) { + console.error('Error: Project name cannot start with a dash (-)'); + process.exit(1); + } + args.splice(i, 2); // Remove both the flag and its value + i--; // Adjust index since we removed 2 elements + } else { + console.error('Error: -p/--project requires a project name'); + process.exit(1); + } + } else if (arg === '-k' || arg === '--api-key') { + if (i + 1 < args.length) { + apiKey = args[i + 1]; + if (apiKey.startsWith('-')) { + console.error('Error: API key cannot start with a dash (-)'); + process.exit(1); + } + args.splice(i, 2); // Remove both the flag and its value + i--; // Adjust index since we removed 2 elements + } else { + console.error('Error: -k/--api-key requires an API key'); + process.exit(1); + } + } + } -// If -y is used without project name, we still need to ask for project name + + +// Validate that if -y is used, a project name must be provided if (autoAcceptDefaults && !projectName) { - // We'll handle this case in the init function by asking only for project name - autoAcceptDefaults = false; + console.error('Error: -y flag requires a project name. Use -p/--project to specify the project name.'); + process.exit(1); } -init(projectName, autoAcceptDefaults).catch((err) => { +init(projectName, autoAcceptDefaults, apiKey).catch((err) => { console.error('Error:', err); process.exit(1); }); diff --git a/bin/init.js b/bin/init.js index 47eb604..0800764 100644 --- a/bin/init.js +++ b/bin/init.js @@ -63,7 +63,7 @@ async function queryNeynarApp(apiKey) { } // Export the main CLI function for programmatic use -export async function init(projectName = null, autoAcceptDefaults = false) { +export async function init(projectName = null, autoAcceptDefaults = false, apiKey = null) { printWelcomeMessage(); // Ask about Neynar usage @@ -101,52 +101,59 @@ export async function init(projectName = null, autoAcceptDefaults = false) { break; } - console.log( - '\n🪐 Find your Neynar API key at: https://dev.neynar.com/app\n' - ); - - let neynarKeyAnswer; - if (autoAcceptDefaults) { - neynarKeyAnswer = { neynarApiKey: null }; + // Use provided API key if available, otherwise prompt for it + if (apiKey) { + neynarApiKey = apiKey; } else { - neynarKeyAnswer = await inquirer.prompt([ - { - type: 'password', - name: 'neynarApiKey', - message: 'Enter your Neynar API key (or press enter to skip):', - default: null, - }, - ]); - } + if (!autoAcceptDefaults) { + console.log( + '\n🪐 Find your Neynar API key at: https://dev.neynar.com/app\n' + ); + } - if (neynarKeyAnswer.neynarApiKey) { - neynarApiKey = neynarKeyAnswer.neynarApiKey; - } else { - let useDemoKey; + let neynarKeyAnswer; if (autoAcceptDefaults) { - useDemoKey = { useDemo: true }; + neynarKeyAnswer = { neynarApiKey: null }; } else { - useDemoKey = await inquirer.prompt([ + neynarKeyAnswer = await inquirer.prompt([ { - type: 'confirm', - name: 'useDemo', - message: 'Would you like to try the demo Neynar API key?', - default: true, + type: 'password', + name: 'neynarApiKey', + message: 'Enter your Neynar API key (or press enter to skip):', + default: null, }, ]); } - if (useDemoKey.useDemo) { - console.warn( - '\n⚠️ Note: the demo key is for development purposes only and is aggressively rate limited.' - ); - console.log( - 'For production, please sign up for a Neynar account at https://neynar.com/ and configure the API key in your .env or .env.local file with NEYNAR_API_KEY.' - ); - console.log( - `\n${purple}${bright}${italic}Neynar now has a free tier! See https://neynar.com/#pricing for details.\n${reset}` - ); - neynarApiKey = 'FARCASTER_V2_FRAMES_DEMO'; + if (neynarKeyAnswer.neynarApiKey) { + neynarApiKey = neynarKeyAnswer.neynarApiKey; + } else { + let useDemoKey; + if (autoAcceptDefaults) { + useDemoKey = { useDemo: true }; + } else { + useDemoKey = await inquirer.prompt([ + { + type: 'confirm', + name: 'useDemo', + message: 'Would you like to try the demo Neynar API key?', + default: true, + }, + ]); + } + + if (useDemoKey.useDemo) { + console.warn( + '\n⚠️ Note: the demo key is for development purposes only and is aggressively rate limited.' + ); + console.log( + 'For production, please sign up for a Neynar account at https://neynar.com/ and configure the API key in your .env or .env.local file with NEYNAR_API_KEY.' + ); + console.log( + `\n${purple}${bright}${italic}Neynar now has a free tier! See https://neynar.com/#pricing for details.\n${reset}` + ); + neynarApiKey = 'FARCASTER_V2_FRAMES_DEMO'; + } } } diff --git a/package.json b/package.json index e893232..d5f5e56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.5.8", + "version": "1.5.9", "type": "module", "private": false, "access": "public",