rn-ai
Version:
Full stack mobile framework for building cross-platform mobile AI apps supporting image processing, real-time / streaming text and chat UIs, and image uploads with multiple service providers.
242 lines (196 loc) • 6.81 kB
JavaScript
import chalk from 'chalk'
import path from 'path'
import fs from 'fs'
import { Command } from 'commander'
import select from '@inquirer/select'
import { input } from '@inquirer/prompts';
import {execa, execaCommand} from 'execa'
import ora from 'ora'
import childProcess from 'child_process'
const log = console.log
const program = new Command()
const green = chalk.green
const repoUrl = 'https://github.com/dabit3/react-native-ai.git'
const isYarnInstalled = () => {
try {
childProcess.execSync('yarn --version');
return true;
} catch {
return false;
}
}
const isBunInstalled = () => {
try {
childProcess.execSync('bun --version')
return true;
} catch(err) {
return false;
}
}
async function main() {
const spinner = ora({
text: 'Creating codebase'
})
try {
const kebabRegez = /^([a-z]+)(-[a-z0-9]+)*$/
program
.name('React Native AI')
.description('Full Stack React Native Boilerplate for building streaming AI apps.')
program.parse(process.argv)
const args = program.args
let appName = args[0]
if (!appName || !kebabRegez.test(args[0])) {
appName = await input({
message: 'Enter your app name',
default: 'rn-ai',
validate: d => {
if(!kebabRegez.test(d)) {
return 'please enter your app name in the format of my-app-name'
}
return true
}
})
}
const withEnv = await select({
message: 'Configure environment variables now?',
choices: [
{
name: 'Yes',
value: 'yes',
},
{
name: 'No',
value: 'no',
}
]
})
let clientEnvs = `
EXPO_PUBLIC_ENV="DEVELOPMENT"
# Your development URL (localhost or ngrok)
EXPO_PUBLIC_DEV_API_URL="http://localhost:3050"
# Your production URL
EXPO_PUBLIC_PROD_API_URL="https://staging.example.com"
`
let envs = `
# environment, either PRODUCTION or DEVELOPMENT
ENVIRONMENT="PRODUCTION"
# OpenAI https://platform.openai.com
OPENAI_API_KEY=""
# FAL AI https://www.fal.ai
FAL_API_KEY=""
# Anthropic (optional) https://console.anthropic.com
ANTHROPIC_API_KEY=""
# Cohere (optional) https://cohere.com
COHERE_API_KEY=""
# ByteScale secret (optional) https://bytescale.com
BYTESCALE_API_KEY=""
# Replicate secret (optional) https://replicate.com/
REPLICATE_KEY=""
# Gemini API Key (optional) https://makersuite.google.com
GEMINI_API_KEY=""
`
if (withEnv === 'yes') {
console.log('Get OpenAI API Key at https://platform.openai.com')
const openai_api_key = await input({ message: "OpenAI API Key" })
console.log('Get Fal API Key at https://www.fal.ai')
const fal_api_key = await input({ message: "Fal API Key" })
console.log('(optional) Get Anthropic API Key at https://console.anthropic.com')
const anthropic_api_key = await input({ message: "Anthropic API Key" })
console.log('(optional) Get Cohere API Key at https://cohere.com')
const cohere_api_key = await input({ message: "Cohere API Key" })
console.log('(optional) Get Bytescale API Key at https://bytescale.com')
const bytescale_api_key = await input({ message: "Bytescale API Key" })
console.log('(optional) Get Replicate API Key at https://replicate.com')
const replicate_api_key = await input({ message: "Replicate API Key" })
console.log('(optional) Get Gemini API Key at https://makersuite.google.com')
const gemeni_api_key = await input({ message: "Gemini API Key" })
envs = `
# environment, either PRODUCTION or DEVELOPMENT
ENVIRONMENT="PRODUCTION"
# ByteScale
BYTESCALE_API_KEY="${bytescale_api_key}"
# OpenAI
OPENAI_API_KEY="${openai_api_key}"
# Anthropic
ANTHROPIC_API_KEY="${anthropic_api_key}"
# Cohere
COHERE_API_KEY="${cohere_api_key}"
# FAL
FAL_API_KEY="${fal_api_key}"
# Replicate secret
REPLICATE_KEY="${replicate_api_key}"
# Gemini API Key
GEMINI_API_KEY=${gemeni_api_key}
`
}
log(`\nInitializing project. \n`)
spinner.start()
await execa('git', ['clone', repoUrl, appName])
try {
await execa('rm', ['-r', `${appName}/cli`])
await execa('rm', ['-rf', `${appName}/.git`])
} catch (err) {}
let packageJson = fs.readFileSync(`${appName}/server/package.json`, 'utf8')
const packageObj = JSON.parse(packageJson)
packageObj.name = appName
packageJson = JSON.stringify(packageObj, null, 2)
fs.writeFileSync(`${appName}/server/package.json`, packageJson)
let packageJson2 = fs.readFileSync(`${appName}/app/package.json`, 'utf8')
const packageObj2 = JSON.parse(packageJson2)
packageObj2.name = appName
packageJson2 = JSON.stringify(packageObj2, null, 2)
fs.writeFileSync(`${appName}/app/package.json`, packageJson2)
fs.writeFileSync(`${appName}/server/.env`, envs)
fs.writeFileSync(`${appName}/app/.env`, clientEnvs)
process.chdir(path.join(process.cwd(), `${appName}/server`))
spinner.text = ''
let serverStartCommand = ''
if (isBunInstalled()) {
spinner.text = 'Installing server dependencies'
await execaCommand('bun install').pipeStdout(process.stdout)
spinner.text = ''
serverStartCommand = 'bun dev'
console.log('\n')
} else if (isYarnInstalled()) {
await execaCommand('yarn').pipeStdout(process.stdout)
serverStartCommand = 'yarn dev'
} else {
spinner.text = 'Installing server dependencies'
await execa('npm', ['install', '--verbose']).pipeStdout(process.stdout)
spinner.text = ''
serverStartCommand = 'npm run dev'
}
process.chdir('../')
process.chdir(path.join(process.cwd(), `app`))
spinner.text = ''
let appStartCommand = ''
if (isBunInstalled()) {
spinner.text = 'Installing app dependencies'
await execaCommand('bun install').pipeStdout(process.stdout)
spinner.text = ''
appStartCommand = 'bun start'
console.log('\n')
} else if (isYarnInstalled()) {
await execaCommand('yarn').pipeStdout(process.stdout)
appStartCommand = 'yarn start'
} else {
spinner.text = 'Installing app dependencies'
await execa('npm', ['install', '--verbose']).pipeStdout(process.stdout)
spinner.text = ''
appStartCommand = 'npm start'
}
spinner.stop()
process.chdir('../')
log(`${green.bold('Success!')} Created ${appName} at ${process.cwd()} \n`)
log(`To get started, change into the server directory and run ${chalk.cyan(serverStartCommand)}\n`)
log(`In a separate terminal, change into the app directory and run ${chalk.cyan(appStartCommand)}`)
} catch (err) {
log('\n')
if (err.exitCode == 128) {
log('Error: directory already exists.')
}
spinner.stop()
}
}
main()