mirror of
https://github.com/ibelick/webclaw.git
synced 2026-08-14 09:02:04 +00:00
refactor(installer): unify install flow with webclaw CLI and restore banner/env prompts
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_URL="https://github.com/ibelick/webclaw"
|
||||
APP_DIR="webclaw"
|
||||
ENV_FILE="apps/webclaw/.env.local"
|
||||
TRACKING_URL="https://collector.onedollarstats.com/events"
|
||||
EVENT_URL="https://webclaw.dev/install"
|
||||
|
||||
@@ -40,8 +38,8 @@ function track_install() {
|
||||
|
||||
track_install
|
||||
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
echo "git is required but not found."
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
echo "npm is required but not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -52,52 +50,13 @@ if ! command -v pnpm >/dev/null 2>&1; then
|
||||
fi
|
||||
|
||||
if [ -d "$APP_DIR" ]; then
|
||||
echo "Directory '$APP_DIR' already exists. Skipping clone."
|
||||
echo "Directory '$APP_DIR' already exists. Skipping init."
|
||||
else
|
||||
git clone "$REPO_URL" "$APP_DIR"
|
||||
npx webclaw init "$APP_DIR"
|
||||
fi
|
||||
|
||||
cd "$APP_DIR"
|
||||
pnpm install
|
||||
|
||||
echo ""
|
||||
if [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
||||
printf "Create %s now? [Y/n]: " "$ENV_FILE" > /dev/tty
|
||||
read -r create_env < /dev/tty
|
||||
else
|
||||
echo "No TTY detected. Skipping env setup prompts."
|
||||
create_env="skip"
|
||||
fi
|
||||
|
||||
if [ -z "$create_env" ] || [ "$create_env" = "y" ] || [ "$create_env" = "Y" ]; then
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
printf "%s already exists. Overwrite? [y/N]: " "$ENV_FILE" > /dev/tty
|
||||
read -r overwrite_env < /dev/tty
|
||||
if [ "$overwrite_env" != "y" ] && [ "$overwrite_env" != "Y" ]; then
|
||||
echo "Keeping existing $ENV_FILE"
|
||||
create_env="skip"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$create_env" != "skip" ]; then
|
||||
printf "CLAWDBOT_GATEWAY_URL: " > /dev/tty
|
||||
read -r gateway_url < /dev/tty
|
||||
printf "CLAWDBOT_GATEWAY_TOKEN: " > /dev/tty
|
||||
read -r gateway_token < /dev/tty
|
||||
|
||||
mkdir -p "$(dirname "$ENV_FILE")"
|
||||
{
|
||||
echo "CLAWDBOT_GATEWAY_URL=$gateway_url"
|
||||
echo "CLAWDBOT_GATEWAY_TOKEN=$gateway_token"
|
||||
} > "$ENV_FILE"
|
||||
|
||||
echo "Wrote $ENV_FILE"
|
||||
fi
|
||||
else
|
||||
echo "Skipping env file. Create it later at $ENV_FILE with:"
|
||||
echo "CLAWDBOT_GATEWAY_URL=..."
|
||||
echo "CLAWDBOT_GATEWAY_TOKEN=..."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Next step: pnpm -C apps/webclaw dev"
|
||||
|
||||
@@ -5,11 +5,21 @@ import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import readline from 'node:readline/promises'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const REPO_URL = 'https://github.com/ibelick/webclaw'
|
||||
|
||||
function printBanner() {
|
||||
process.stdout.write(` ▄▄ ▄▄ \n`)
|
||||
process.stdout.write(` ██ ██ \n`)
|
||||
process.stdout.write(`██ ██ ▄█▀█▄ ████▄ ▄████ ██ ▀▀█▄ ██ ██ \n`)
|
||||
process.stdout.write(`██ █ ██ ██▄█▀ ██ ██ ██ ██ ▄█▀██ ██ █ ██ \n`)
|
||||
process.stdout.write(` ██▀██ ▀█▄▄▄ ████▀ ▀████ ██ ▀█▄██ ██▀██ \n\n`)
|
||||
process.stdout.write(`Fast web client for OpenClaw\n\n`)
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
process.stdout.write(`webclaw CLI\n\n`)
|
||||
process.stdout.write(`Usage:\n`)
|
||||
@@ -23,6 +33,7 @@ function printHelp() {
|
||||
process.stdout.write(` webclaw doctor Validate local setup\n`)
|
||||
process.stdout.write(`\nOptions:\n`)
|
||||
process.stdout.write(` --force Allow init in non-empty directory\n`)
|
||||
process.stdout.write(` --skip-env Skip .env.local setup prompts\n`)
|
||||
process.stdout.write(` -h, --help Show help\n`)
|
||||
}
|
||||
|
||||
@@ -121,7 +132,78 @@ function cloneRepo(targetDir) {
|
||||
runCommand('git', ['clone', '--depth', '1', REPO_URL, targetDir], process.cwd())
|
||||
}
|
||||
|
||||
function initProject(rawTarget, options) {
|
||||
function resolveEnvFile(targetDir) {
|
||||
const monorepoEnv = path.join(targetDir, 'apps', 'webclaw', '.env.local')
|
||||
if (fs.existsSync(path.join(targetDir, 'apps', 'webclaw'))) {
|
||||
return monorepoEnv
|
||||
}
|
||||
return path.join(targetDir, '.env.local')
|
||||
}
|
||||
|
||||
async function askQuestion(rl, question) {
|
||||
const answer = await rl.question(question)
|
||||
return answer.trim()
|
||||
}
|
||||
|
||||
async function maybeSetupEnv(targetDir, options) {
|
||||
if (options.has('--skip-env')) return
|
||||
if (!process.stdin.isTTY || !process.stdout.isTTY) return
|
||||
|
||||
const envFile = resolveEnvFile(targetDir)
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
})
|
||||
|
||||
try {
|
||||
const createAnswer = await askQuestion(
|
||||
rl,
|
||||
`Create ${envFile} now? [Y/n]: `,
|
||||
)
|
||||
const shouldCreate =
|
||||
createAnswer.length === 0 ||
|
||||
createAnswer.toLowerCase() === 'y' ||
|
||||
createAnswer.toLowerCase() === 'yes'
|
||||
|
||||
if (!shouldCreate) {
|
||||
process.stdout.write(
|
||||
`Skipping env file. Create it later at ${envFile} with:\n` +
|
||||
`CLAWDBOT_GATEWAY_URL=...\n` +
|
||||
`CLAWDBOT_GATEWAY_TOKEN=...\n\n`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (fs.existsSync(envFile)) {
|
||||
const overwriteAnswer = await askQuestion(
|
||||
rl,
|
||||
`${envFile} already exists. Overwrite? [y/N]: `,
|
||||
)
|
||||
const shouldOverwrite =
|
||||
overwriteAnswer.toLowerCase() === 'y' ||
|
||||
overwriteAnswer.toLowerCase() === 'yes'
|
||||
if (!shouldOverwrite) {
|
||||
process.stdout.write(`Keeping existing ${envFile}\n\n`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const gatewayUrl = await askQuestion(rl, 'CLAWDBOT_GATEWAY_URL: ')
|
||||
const gatewayToken = await askQuestion(rl, 'CLAWDBOT_GATEWAY_TOKEN: ')
|
||||
|
||||
ensureDir(path.dirname(envFile))
|
||||
fs.writeFileSync(
|
||||
envFile,
|
||||
`CLAWDBOT_GATEWAY_URL=${gatewayUrl}\nCLAWDBOT_GATEWAY_TOKEN=${gatewayToken}\n`,
|
||||
)
|
||||
process.stdout.write(`Wrote ${envFile}\n\n`)
|
||||
} finally {
|
||||
rl.close()
|
||||
}
|
||||
}
|
||||
|
||||
async function initProject(rawTarget, options) {
|
||||
printBanner()
|
||||
const targetDir = path.resolve(process.cwd(), rawTarget ?? '.')
|
||||
const force = options.has('--force')
|
||||
const isCurrentDir = targetDir === process.cwd()
|
||||
@@ -160,6 +242,8 @@ function initProject(rawTarget, options) {
|
||||
}
|
||||
}
|
||||
|
||||
await maybeSetupEnv(targetDir, options)
|
||||
|
||||
process.stdout.write(`\nWebClaw project created at ${targetDir}\n\n`)
|
||||
process.stdout.write(`Next steps:\n`)
|
||||
process.stdout.write(` cd ${path.relative(process.cwd(), targetDir) || '.'}\n`)
|
||||
@@ -190,7 +274,7 @@ function doctor() {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
function main() {
|
||||
async function main() {
|
||||
const args = process.argv.slice(2)
|
||||
const options = new Set(args.filter((arg) => arg.startsWith('-')))
|
||||
const command = args.find((arg) => !arg.startsWith('-'))
|
||||
@@ -201,7 +285,7 @@ function main() {
|
||||
}
|
||||
|
||||
if (!command) {
|
||||
initProject('.', options)
|
||||
await initProject('.', options)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -211,7 +295,7 @@ function main() {
|
||||
const previous = args[index - 1]
|
||||
return previous === 'init'
|
||||
})
|
||||
initProject(target, options)
|
||||
await initProject(target, options)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -236,4 +320,9 @@ function main() {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
main()
|
||||
void main().catch((error) => {
|
||||
process.stderr.write(
|
||||
`${error instanceof Error ? error.message : String(error)}\n`,
|
||||
)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user