mirror of
https://github.com/eggent-ai/eggent.git
synced 2026-03-07 18:13:07 +00:00
Initial commit
This commit is contained in:
216
scripts/install-docker.sh
Executable file
216
scripts/install-docker.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
ENV_EXAMPLE_FILE="$ROOT_DIR/.env.example"
|
||||
DOCKER_BIN="${DOCKER_BIN:-docker}"
|
||||
read -r -a DOCKER_CMD <<<"$DOCKER_BIN"
|
||||
|
||||
require_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "Missing dependency: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
upsert_env() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local tmp found line
|
||||
tmp="$(mktemp)"
|
||||
found=0
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ "$line" == "$key="* ]]; then
|
||||
printf '%s=%s\n' "$key" "$value" >>"$tmp"
|
||||
found=1
|
||||
else
|
||||
printf '%s\n' "$line" >>"$tmp"
|
||||
fi
|
||||
done <"$file"
|
||||
fi
|
||||
|
||||
if [[ "$found" -eq 0 ]]; then
|
||||
printf '%s=%s\n' "$key" "$value" >>"$tmp"
|
||||
fi
|
||||
|
||||
mv "$tmp" "$file"
|
||||
}
|
||||
|
||||
get_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
grep -E "^${key}=" "$file" | tail -n 1 | cut -d= -f2- || true
|
||||
}
|
||||
|
||||
looks_placeholder() {
|
||||
local value="$1"
|
||||
if [[ -z "$value" ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$value" in
|
||||
replace-with-* | *replace-with* | changeme | example | ... )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
random_hex() {
|
||||
local bytes="${1:-32}"
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex "$bytes"
|
||||
return 0
|
||||
fi
|
||||
|
||||
node -e "process.stdout.write(require('node:crypto').randomBytes(${bytes}).toString('hex'))"
|
||||
}
|
||||
|
||||
wait_for_health() {
|
||||
local url="$1"
|
||||
local retries="$2"
|
||||
local delay="$3"
|
||||
local i
|
||||
|
||||
for i in $(seq 1 "$retries"); do
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if curl --silent --show-error --fail "$url" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if node -e "fetch(process.argv[1]).then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" "$url"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
sleep "$delay"
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
docker_cmd() {
|
||||
"${DOCKER_CMD[@]}" "$@"
|
||||
}
|
||||
|
||||
prepare_data_dir() {
|
||||
local data_dir="$ROOT_DIR/data"
|
||||
mkdir -p "$data_dir"
|
||||
|
||||
# The runtime container runs as user "node" (uid/gid 1000).
|
||||
# If setup is executed as root, fix bind-mount ownership to avoid EACCES at runtime.
|
||||
if [[ "$(id -u)" -eq 0 ]]; then
|
||||
chown -R 1000:1000 "$data_dir" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ ! -w "$data_dir" ]]; then
|
||||
echo "WARNING: $data_dir is not writable. App may fail with 500 on /dashboard." >&2
|
||||
echo "Run: sudo chown -R 1000:1000 $data_dir" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_data_dir_writable_for_runtime() {
|
||||
local data_dir="$ROOT_DIR/data"
|
||||
|
||||
if docker_cmd run --rm --user 1000:1000 -v "$data_dir:/target" eggent:local \
|
||||
sh -lc "test -w /target" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
docker_cmd run --rm --user 0:0 -v "$data_dir:/target" eggent:local \
|
||||
sh -lc "chown -R 1000:1000 /target" >/dev/null 2>&1 || true
|
||||
|
||||
if docker_cmd run --rm --user 1000:1000 -v "$data_dir:/target" eggent:local \
|
||||
sh -lc "test -w /target" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "ERROR: data directory is not writable for runtime user (uid 1000)." >&2
|
||||
echo "Fix and rerun:" >&2
|
||||
echo " sudo chown -R 1000:1000 $data_dir" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "==> Docker setup (isolated)"
|
||||
require_cmd "${DOCKER_CMD[0]}"
|
||||
if ! docker_cmd compose version >/dev/null 2>&1; then
|
||||
echo "Docker Compose v2 is required (docker compose ...)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
if [[ ! -f "$ENV_EXAMPLE_FILE" ]]; then
|
||||
echo "Missing template file: $ENV_EXAMPLE_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$ENV_EXAMPLE_FILE" "$ENV_FILE"
|
||||
echo "Created .env from .env.example"
|
||||
fi
|
||||
|
||||
EXTERNAL_API_TOKEN_VALUE="$(get_env_value "$ENV_FILE" "EXTERNAL_API_TOKEN")"
|
||||
if looks_placeholder "$EXTERNAL_API_TOKEN_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "EXTERNAL_API_TOKEN" "$(random_hex 32)"
|
||||
echo "Generated EXTERNAL_API_TOKEN in .env"
|
||||
fi
|
||||
|
||||
TELEGRAM_WEBHOOK_SECRET_VALUE="$(get_env_value "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET")"
|
||||
if looks_placeholder "$TELEGRAM_WEBHOOK_SECRET_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET" "$(random_hex 24)"
|
||||
echo "Generated TELEGRAM_WEBHOOK_SECRET in .env"
|
||||
fi
|
||||
|
||||
EGGENT_AUTH_SECRET_VALUE="$(get_env_value "$ENV_FILE" "EGGENT_AUTH_SECRET")"
|
||||
if looks_placeholder "$EGGENT_AUTH_SECRET_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "EGGENT_AUTH_SECRET" "$(random_hex 32)"
|
||||
echo "Generated EGGENT_AUTH_SECRET in .env"
|
||||
fi
|
||||
|
||||
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
||||
prepare_data_dir
|
||||
|
||||
APP_PORT="${APP_PORT:-$(get_env_value "$ENV_FILE" "APP_PORT")}"
|
||||
APP_PORT="${APP_PORT:-3000}"
|
||||
APP_BIND_HOST="${APP_BIND_HOST:-$(get_env_value "$ENV_FILE" "APP_BIND_HOST")}"
|
||||
APP_BIND_HOST="${APP_BIND_HOST:-127.0.0.1}"
|
||||
upsert_env "$ENV_FILE" "APP_PORT" "$APP_PORT"
|
||||
upsert_env "$ENV_FILE" "APP_BIND_HOST" "$APP_BIND_HOST"
|
||||
HEALTH_URL="http://127.0.0.1:${APP_PORT}/api/health"
|
||||
|
||||
echo "==> Building image"
|
||||
docker_cmd compose build app
|
||||
|
||||
echo "==> Verifying data directory permissions"
|
||||
ensure_data_dir_writable_for_runtime
|
||||
|
||||
echo "==> Starting container"
|
||||
docker_cmd compose up -d app
|
||||
|
||||
echo "==> Waiting for health: $HEALTH_URL"
|
||||
if ! wait_for_health "$HEALTH_URL" 90 1; then
|
||||
echo "Container did not become healthy. Recent logs:" >&2
|
||||
docker_cmd compose logs --tail 120 app >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Docker setup complete."
|
||||
echo "App URL:"
|
||||
if [[ "$APP_BIND_HOST" == "0.0.0.0" ]]; then
|
||||
echo " http://<server-ip>:${APP_PORT}"
|
||||
else
|
||||
echo " http://localhost:${APP_PORT}"
|
||||
fi
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " docker compose logs -f app"
|
||||
echo " docker compose restart app"
|
||||
echo " docker compose down"
|
||||
195
scripts/install-local.sh
Executable file
195
scripts/install-local.sh
Executable file
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENV_FILE="$ROOT_DIR/.env"
|
||||
ENV_EXAMPLE_FILE="$ROOT_DIR/.env.example"
|
||||
|
||||
require_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "Missing dependency: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
warn_if_missing_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "WARNING: Recommended utility is missing: $1" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
check_node_version() {
|
||||
local version major
|
||||
version="$(node -p "process.versions.node")"
|
||||
major="${version%%.*}"
|
||||
if [[ "$major" -lt 20 ]]; then
|
||||
echo "Node.js 20+ is required (found $version)." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
upsert_env() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local tmp found line
|
||||
tmp="$(mktemp)"
|
||||
found=0
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ "$line" == "$key="* ]]; then
|
||||
printf '%s=%s\n' "$key" "$value" >>"$tmp"
|
||||
found=1
|
||||
else
|
||||
printf '%s\n' "$line" >>"$tmp"
|
||||
fi
|
||||
done <"$file"
|
||||
fi
|
||||
|
||||
if [[ "$found" -eq 0 ]]; then
|
||||
printf '%s=%s\n' "$key" "$value" >>"$tmp"
|
||||
fi
|
||||
|
||||
mv "$tmp" "$file"
|
||||
}
|
||||
|
||||
get_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
grep -E "^${key}=" "$file" | tail -n 1 | cut -d= -f2- || true
|
||||
}
|
||||
|
||||
looks_placeholder() {
|
||||
local value="$1"
|
||||
if [[ -z "$value" ]]; then
|
||||
return 0
|
||||
fi
|
||||
case "$value" in
|
||||
replace-with-* | *replace-with* | changeme | example | ... )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
random_hex() {
|
||||
local bytes="${1:-32}"
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex "$bytes"
|
||||
return 0
|
||||
fi
|
||||
|
||||
node -e "process.stdout.write(require('node:crypto').randomBytes(${bytes}).toString('hex'))"
|
||||
}
|
||||
|
||||
wait_for_health() {
|
||||
local url="$1"
|
||||
local retries="$2"
|
||||
local delay="$3"
|
||||
local i
|
||||
|
||||
for i in $(seq 1 "$retries"); do
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if curl --silent --show-error --fail "$url" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if node -e "fetch(process.argv[1]).then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" "$url"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
sleep "$delay"
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "==> Local production setup (npm)"
|
||||
require_cmd node
|
||||
require_cmd npm
|
||||
require_cmd python3
|
||||
require_cmd curl
|
||||
check_node_version
|
||||
|
||||
warn_if_missing_cmd git
|
||||
warn_if_missing_cmd jq
|
||||
warn_if_missing_cmd pip3
|
||||
warn_if_missing_cmd rg
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
if [[ ! -f "$ENV_EXAMPLE_FILE" ]]; then
|
||||
echo "Missing template file: $ENV_EXAMPLE_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$ENV_EXAMPLE_FILE" "$ENV_FILE"
|
||||
echo "Created .env from .env.example"
|
||||
fi
|
||||
|
||||
EXTERNAL_API_TOKEN_VALUE="$(get_env_value "$ENV_FILE" "EXTERNAL_API_TOKEN")"
|
||||
if looks_placeholder "$EXTERNAL_API_TOKEN_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "EXTERNAL_API_TOKEN" "$(random_hex 32)"
|
||||
echo "Generated EXTERNAL_API_TOKEN in .env"
|
||||
fi
|
||||
|
||||
TELEGRAM_WEBHOOK_SECRET_VALUE="$(get_env_value "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET")"
|
||||
if looks_placeholder "$TELEGRAM_WEBHOOK_SECRET_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET" "$(random_hex 24)"
|
||||
echo "Generated TELEGRAM_WEBHOOK_SECRET in .env"
|
||||
fi
|
||||
|
||||
EGGENT_AUTH_SECRET_VALUE="$(get_env_value "$ENV_FILE" "EGGENT_AUTH_SECRET")"
|
||||
if looks_placeholder "$EGGENT_AUTH_SECRET_VALUE"; then
|
||||
upsert_env "$ENV_FILE" "EGGENT_AUTH_SECRET" "$(random_hex 32)"
|
||||
echo "Generated EGGENT_AUTH_SECRET in .env"
|
||||
fi
|
||||
|
||||
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
||||
mkdir -p "$ROOT_DIR/data"
|
||||
|
||||
echo "==> Installing dependencies"
|
||||
npm install --no-package-lock
|
||||
|
||||
echo "==> Building production bundle"
|
||||
npm run build
|
||||
|
||||
HEALTH_PORT="${HEALTH_PORT:-3077}"
|
||||
HEALTH_URL="http://127.0.0.1:${HEALTH_PORT}/api/health"
|
||||
START_LOG="$ROOT_DIR/.install-local-start.log"
|
||||
|
||||
echo "==> Running smoke check: $HEALTH_URL"
|
||||
PORT="$HEALTH_PORT" HOSTNAME="127.0.0.1" npm run start >"$START_LOG" 2>&1 &
|
||||
START_PID=$!
|
||||
|
||||
cleanup() {
|
||||
if kill -0 "$START_PID" >/dev/null 2>&1; then
|
||||
kill "$START_PID" >/dev/null 2>&1 || true
|
||||
wait "$START_PID" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if ! wait_for_health "$HEALTH_URL" 45 1; then
|
||||
echo "Smoke check failed. Server log tail:" >&2
|
||||
tail -n 120 "$START_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$START_LOG"
|
||||
cleanup
|
||||
trap - EXIT
|
||||
|
||||
echo ""
|
||||
echo "Setup complete."
|
||||
echo "Run in production mode:"
|
||||
echo " npm run start"
|
||||
echo ""
|
||||
echo "App URL:"
|
||||
echo " http://127.0.0.1:3000"
|
||||
229
scripts/install.sh
Executable file
229
scripts/install.sh
Executable file
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_URL="${EGGENT_REPO_URL:-https://github.com/eggent-ai/eggent.git}"
|
||||
BRANCH="${EGGENT_BRANCH:-main}"
|
||||
INSTALL_DIR="${EGGENT_INSTALL_DIR:-$HOME/.eggent}"
|
||||
AUTO_INSTALL_DOCKER="${EGGENT_AUTO_INSTALL_DOCKER:-1}"
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_root() {
|
||||
if [[ "$(id -u)" -eq 0 ]]; then
|
||||
"$@"
|
||||
else
|
||||
if ! command -v sudo >/dev/null 2>&1; then
|
||||
fail "sudo is required to install system packages"
|
||||
fi
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
docker_compose_ready() {
|
||||
if command_exists docker && docker compose version >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command_exists sudo && command_exists docker && sudo docker compose version >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
install_curl_if_missing() {
|
||||
if command_exists curl; then
|
||||
return
|
||||
fi
|
||||
|
||||
if command_exists apt-get; then
|
||||
run_root apt-get update
|
||||
run_root apt-get install -y curl
|
||||
elif command_exists dnf; then
|
||||
run_root dnf install -y curl
|
||||
elif command_exists yum; then
|
||||
run_root yum install -y curl
|
||||
else
|
||||
fail "curl is required for Docker fallback install"
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker_via_official_script() {
|
||||
local tmp_script
|
||||
install_curl_if_missing
|
||||
tmp_script="$(mktemp)"
|
||||
curl -fsSL https://get.docker.com -o "$tmp_script"
|
||||
run_root sh "$tmp_script"
|
||||
rm -f "$tmp_script"
|
||||
}
|
||||
|
||||
install_git_if_missing() {
|
||||
if command_exists git; then
|
||||
return
|
||||
fi
|
||||
|
||||
local os
|
||||
os="$(uname -s)"
|
||||
log "==> Installing git"
|
||||
|
||||
case "$os" in
|
||||
Darwin)
|
||||
command_exists brew || fail "Homebrew is required to install git automatically"
|
||||
brew install git
|
||||
;;
|
||||
Linux)
|
||||
if command_exists apt-get; then
|
||||
run_root apt-get update
|
||||
run_root apt-get install -y git
|
||||
elif command_exists dnf; then
|
||||
run_root dnf install -y git
|
||||
elif command_exists yum; then
|
||||
run_root yum install -y git
|
||||
else
|
||||
fail "Unsupported Linux package manager for git auto-install"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
fail "Unsupported OS: $os"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_docker_if_missing() {
|
||||
if docker_compose_ready; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$AUTO_INSTALL_DOCKER" != "1" ]]; then
|
||||
fail "Docker is not installed. Install Docker manually and rerun."
|
||||
fi
|
||||
|
||||
local os
|
||||
os="$(uname -s)"
|
||||
log "==> Installing Docker (best-effort)"
|
||||
|
||||
case "$os" in
|
||||
Darwin)
|
||||
command_exists brew || fail "Homebrew is required for automatic Docker install on macOS"
|
||||
brew install --cask docker
|
||||
if command_exists open; then
|
||||
open -a Docker >/dev/null 2>&1 || true
|
||||
fi
|
||||
;;
|
||||
Linux)
|
||||
if command_exists apt-get; then
|
||||
run_root apt-get update
|
||||
if ! run_root apt-get install -y docker.io docker-compose-plugin; then
|
||||
log "==> docker-compose-plugin is unavailable, trying docker-compose-v2"
|
||||
if ! run_root apt-get install -y docker.io docker-compose-v2; then
|
||||
log "==> distro Docker packages did not provide Compose v2"
|
||||
fi
|
||||
fi
|
||||
elif command_exists dnf; then
|
||||
run_root dnf install -y docker docker-compose-plugin || true
|
||||
elif command_exists yum; then
|
||||
run_root yum install -y docker docker-compose-plugin || true
|
||||
else
|
||||
fail "Unsupported Linux package manager for Docker auto-install"
|
||||
fi
|
||||
if command_exists systemctl; then
|
||||
run_root systemctl enable --now docker >/dev/null 2>&1 || true
|
||||
fi
|
||||
if ! docker_compose_ready; then
|
||||
log "==> Docker Compose v2 is still unavailable, trying official Docker installer"
|
||||
install_docker_via_official_script
|
||||
if command_exists systemctl; then
|
||||
run_root systemctl enable --now docker >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
fail "Unsupported OS: $os"
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! docker_compose_ready; then
|
||||
fail "Docker was installed but Compose v2 is unavailable. Install Docker manually and verify: docker compose version"
|
||||
fi
|
||||
}
|
||||
|
||||
pick_docker_bin() {
|
||||
if docker info >/dev/null 2>&1; then
|
||||
printf '%s' "docker"
|
||||
return
|
||||
fi
|
||||
|
||||
if sudo docker info >/dev/null 2>&1; then
|
||||
printf '%s' "sudo docker"
|
||||
return
|
||||
fi
|
||||
|
||||
fail "Docker daemon is not available. Start Docker Desktop/service and rerun."
|
||||
}
|
||||
|
||||
ensure_repo() {
|
||||
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
||||
log "==> Updating existing repo in $INSTALL_DIR"
|
||||
git -C "$INSTALL_DIR" fetch origin "$BRANCH" --depth 1
|
||||
git -C "$INSTALL_DIR" checkout "$BRANCH"
|
||||
git -C "$INSTALL_DIR" pull --ff-only origin "$BRANCH"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -d "$INSTALL_DIR" ]]; then
|
||||
fail "Directory exists and is not a git repo: $INSTALL_DIR"
|
||||
fi
|
||||
|
||||
log "==> Cloning repo to $INSTALL_DIR"
|
||||
git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
|
||||
}
|
||||
|
||||
main() {
|
||||
log "==> Eggent one-command installer"
|
||||
log "Repo: $REPO_URL"
|
||||
log "Branch: $BRANCH"
|
||||
log "Install dir: $INSTALL_DIR"
|
||||
|
||||
install_git_if_missing
|
||||
install_docker_if_missing
|
||||
ensure_repo
|
||||
|
||||
local docker_bin
|
||||
local default_bind_host app_bind_host app_port
|
||||
app_port="${APP_PORT:-3000}"
|
||||
default_bind_host="127.0.0.1"
|
||||
if [[ "$(uname -s)" == "Linux" ]]; then
|
||||
# One-command installs are often used on VPS hosts where the app should be reachable remotely.
|
||||
default_bind_host="0.0.0.0"
|
||||
fi
|
||||
app_bind_host="${EGGENT_APP_BIND_HOST:-$default_bind_host}"
|
||||
|
||||
docker_bin="$(pick_docker_bin)"
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
chmod +x ./scripts/install-docker.sh
|
||||
|
||||
log "==> Running Docker deployment"
|
||||
APP_BIND_HOST="$app_bind_host" APP_PORT="$app_port" DOCKER_BIN="$docker_bin" ./scripts/install-docker.sh
|
||||
|
||||
log ""
|
||||
log "Done."
|
||||
if [[ "$app_bind_host" == "0.0.0.0" ]]; then
|
||||
log "Open: http://<server-ip>:${app_port}"
|
||||
else
|
||||
log "Open: http://127.0.0.1:${app_port}"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
79
scripts/test-memory-ingestion.ts
Normal file
79
scripts/test-memory-ingestion.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import { importKnowledge, queryKnowledge } from "../src/lib/memory/knowledge";
|
||||
import { AppSettings } from "../src/lib/types";
|
||||
|
||||
// Mock settings
|
||||
const mockSettings: AppSettings = {
|
||||
chatModel: { provider: "openai", model: "gpt-4o" },
|
||||
utilityModel: { provider: "openai", model: "gpt-4o-mini" },
|
||||
embeddingsModel: {
|
||||
provider: "mock",
|
||||
model: "text-embedding-3-small",
|
||||
// Assumes OPENAI_API_KEY is set in env
|
||||
},
|
||||
codeExecution: { enabled: false, timeout: 30, maxOutputLength: 1000 },
|
||||
memory: {
|
||||
enabled: true,
|
||||
similarityThreshold: 0.1, // Low threshold for testing
|
||||
maxResults: 5,
|
||||
chunkSize: 400,
|
||||
},
|
||||
search: { enabled: false, provider: "none" },
|
||||
general: { darkMode: true, language: "en" },
|
||||
auth: {
|
||||
enabled: true,
|
||||
username: "admin",
|
||||
passwordHash: "",
|
||||
mustChangeCredentials: false,
|
||||
},
|
||||
};
|
||||
|
||||
async function main() {
|
||||
console.log("Starting Memory Ingestion Test...");
|
||||
|
||||
const testDir = path.join(process.cwd(), "data", "test-knowledge");
|
||||
const testSubdir = "test-project-memory";
|
||||
|
||||
// 1. Setup Test Environment
|
||||
console.log("Setting up test directory:", testDir);
|
||||
await fs.mkdir(testDir, { recursive: true });
|
||||
|
||||
// 2. Create Test Files
|
||||
// Text file
|
||||
await fs.writeFile(path.join(testDir, "test.txt"), "The secret code for the project is ALPHA-BETA-GAMMA. It is very confidential.");
|
||||
console.log("Created test.txt");
|
||||
|
||||
// PDF & Image would require real files to test properly,
|
||||
// but we can at least test the text loader and the import logic.
|
||||
// We'll skip creating dummy PDFs/Images for this automated run to avoid binary complexity,
|
||||
// relying on the text test to prove the pipeline works.
|
||||
|
||||
// 3. Import Knowledge
|
||||
console.log("Importing knowledge...");
|
||||
const result = await importKnowledge(testDir, testSubdir, mockSettings);
|
||||
console.log("Import result:", result);
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
console.error("Import errors:", result.errors);
|
||||
}
|
||||
|
||||
// 4. Query Knowledge
|
||||
console.log("Querying knowledge...");
|
||||
const query = "What is the secret code?";
|
||||
const answer = await queryKnowledge(query, 3, [testSubdir], mockSettings);
|
||||
console.log(`Query: "${query}"`);
|
||||
console.log("Result:", answer);
|
||||
|
||||
// 5. Cleanup
|
||||
console.log("Cleaning up...");
|
||||
await fs.rm(testDir, { recursive: true, force: true });
|
||||
// We might want to keep the vector DB for inspection, but strict cleanup removes it too.
|
||||
// const memoryDir = path.join(process.cwd(), "data", "memory", testSubdir);
|
||||
// await fs.rm(memoryDir, { recursive: true, force: true });
|
||||
|
||||
console.log("Test Complete.");
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user