PlanOpticon

feat: cross-platform install scripts for one-command setup install.sh for macOS/Linux and install.ps1 for Windows. Handles Python, FFmpeg, pip install, optional Ollama setup, and API key configuration. Closes #107

lmata 2026-03-08 00:43 trunk
Commit d9ebd871b995017574dcb19034974515ac650b8fa36029a754211a61453aa98a
2 files changed +180 +375
+180
--- a/install.ps1
+++ b/install.ps1
@@ -0,0 +1,180 @@
1
+# PlanOpticon installer for Windows (PowerShell)
2
+# Usage: irm https://planopticon.dev/install.ps1 | iex
3
+
4
+$ErrorActionPreference = "Stop"
5
+
6
+function Write-Step($msg) { Write-Host "`n==> $msg" -ForegroundColor White }
7
+function Write-Ok($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
8
+function Write-Warn($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
9
+function Write-Err($msg) { Write-Host "[error] $msg" -ForegroundColor Red }
10
+function Write-Info($msg) { Write-Host "[info] $msg" -ForegroundColor Blue }
11
+
12
+function Test-Command($cmd) {
13
+ try { Get-Command $cmd -ErrorAction Stop | Out-Null; return $true }
14
+ catch { return $false }
15
+}
16
+
17
+# --- Header ------------------------------------------------------------------
18
+
19
+Write-Host "`nPlanOpticon Installer" -ForegroundColor White
20
+Write-Host "=====================================" -ForegroundColor White
21
+
22
+# --- Python ------------------------------------------------------------------
23
+
24
+Write-Step "Checking Python"
25
+
26
+$python = $null
27
+foreach ($cmd in @("python", "python3", "py")) {
28
+ if (Test-Command $cmd) {
29
+ $ver = & $cmd -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>$null
30
+ if ($ver) {
31
+ $parts = $ver.Split(".")
32
+ if ([int]$parts[0] -ge 3 -and [int]$parts[1] -ge 10) {
33
+ $python = $cmd
34
+ Write-Ok "Python $ver found ($cmd)"
35
+ break
36
+ }
37
+ }
38
+ }
39
+}
40
+
41
+if (-not $python) {
42
+ Write-Warn "Python 3.10+ not found"
43
+ if (Test-Command "winget") {
44
+ Write-Info "Installing Python via winget..."
45
+ winget install Python.Python.3.12 --accept-source-agreements --accept-package-agreements
46
+ $python = "python"
47
+ } elseif (Test-Command "choco") {
48
+ Write-Info "Installing Python via Chocolatey..."
49
+ choco install python312 -y
50
+ $python = "python"
51
+ } else {
52
+ Write-Err "Please install Python 3.10+ from https://www.python.org/downloads/"
53
+ exit 1
54
+ }
55
+ # Refresh PATH
56
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
57
+ Write-Ok "Python installed"
58
+}
59
+
60
+# --- FFmpeg ------------------------------------------------------------------
61
+
62
+Write-Step "Checking FFmpeg"
63
+
64
+if (Test-Command "ffmpeg") {
65
+ Write-Ok "FFmpeg found"
66
+} else {
67
+ Write-Warn "FFmpeg not found"
68
+ if (Test-Command "winget") {
69
+ Write-Info "Installing FFmpeg via winget..."
70
+ winget install Gyan.FFmpeg --accept-source-agreements --accept-package-agreements
71
+ } elseif (Test-Command "choco") {
72
+ Write-Info "Installing FFmpeg via Chocolatey..."
73
+ choco install ffmpeg -y
74
+ } else {
75
+ Write-Err "Please install FFmpeg from https://ffmpeg.org/download.html"
76
+ exit 1
77
+ }
78
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
79
+ Write-Ok "FFmpeg installed"
80
+}
81
+
82
+# --- Extras ------------------------------------------------------------------
83
+
84
+Write-Step "Choose extras"
85
+Write-Host " 1) core - just the basics (default)"
86
+Write-Host " 2) cloud - Google Drive, Dropbox, S3"
87
+Write-Host " 3) pdf - PDF document ingestion"
88
+Write-Host " 4) sources - YouTube, RSS, web scraping"
89
+Write-Host " 5) all - everything"
90
+Write-Host ""
91
+
92
+$choice = Read-Host "Choose extras [1-5, default=1]"
93
+$extras = switch ($choice) {
94
+ "2" { "[cloud]" }
95
+ "3" { "[pdf]" }
96
+ "4" { "[sources]" }
97
+ "5" { "[all]" }
98
+ default { "" }
99
+}
100
+
101
+# --- Install PlanOpticon -----------------------------------------------------
102
+
103
+Write-Step "Installing PlanOpticon"
104
+
105
+$target = "planopticon$extras"
106
+Write-Info "Installing: $target"
107
+
108
+try {
109
+ & $python -m pip install --upgrade $target
110
+ Write-Ok "PlanOpticon installed"
111
+} catch {
112
+ Write-Warn "pip install failed, trying with --user"
113
+ & $python -m pip install --user --upgrade $target
114
+ Write-Ok "PlanOpticon installed (user)"
115
+}
116
+
117
+# --- API key setup -----------------------------------------------------------
118
+
119
+Write-Step "API key setup"
120
+
121
+$envFile = ".env"
122
+if (Test-Path $envFile) {
123
+ Write-Ok "Found existing .env file"
124
+} else {
125
+ Write-Host "`nPlanOpticon needs at least one AI provider API key."
126
+ Write-Host " 1) OpenAI (OPENAI_API_KEY)"
127
+ Write-Host " 2) Anthropic (ANTHROPIC_API_KEY)"
128
+ Write-Host " 3) Google (GEMINI_API_KEY)"
129
+ Write-Host " 4) Ollama (local, no key needed)"
130
+ Write-Host " 5) Skip for now"
131
+ Write-Host ""
132
+
133
+ $providerChoice = Read-Host "Choose provider [1-5]"
134
+ switch ($providerChoice) {
135
+ "1" {
136
+ $key = Read-Host "Enter your OpenAI API key"
137
+ if ($key) { "OPENAI_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
138
+ }
139
+ "2" {
140
+ $key = Read-Host "Enter your Anthropic API key"
141
+ if ($key) { "ANTHROPIC_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
142
+ }
143
+ "3" {
144
+ $key = Read-Host "Enter your Google/Gemini API key"
145
+ if ($key) { "GEMINI_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
146
+ }
147
+ "4" {
148
+ "OLLAMA_HOST=http://localhost:11434" | Out-File $envFile -Encoding utf8
149
+ Write-Info "Using Ollama - no API key needed"
150
+ }
151
+ default { Write-Warn "Skipping API key setup. Add keys to .env later." }
152
+ }
153
+}
154
+
155
+# --- Verify ------------------------------------------------------------------
156
+
157
+Write-Step "Verifying installation"
158
+
159
+if (Test-Command "planopticon") {
160
+ try {
161
+ $version = & planopticon --version 2>$null
162
+ Write-Ok "planopticon CLI ready ($version)"
163
+ } catch {
164
+ Write-Ok "planopticon CLI ready"
165
+ }
166
+} else {
167
+ Write-Warn "planopticon not in PATH - restart your terminal"
168
+}
169
+
170
+# --- Done --------------------------------------------------------------------
171
+
172
+Write-Host "`nInstallation complete!" -ForegroundColor Green
173
+Write-Host ""
174
+Write-Host "Quick start:"
175
+Write-Host " planopticon process video.mp4 # Analyze a video"
176
+Write-Host " planopticon companion # Start AI companion"
177
+Write-Host " planopticon list-models # Check available models"
178
+Write-Host ""
179
+Write-Host "Docs: https://planopticon.dev"
180
+Write-Host ""
--- a/install.ps1
+++ b/install.ps1
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/install.ps1
+++ b/install.ps1
@@ -0,0 +1,180 @@
1 # PlanOpticon installer for Windows (PowerShell)
2 # Usage: irm https://planopticon.dev/install.ps1 | iex
3
4 $ErrorActionPreference = "Stop"
5
6 function Write-Step($msg) { Write-Host "`n==> $msg" -ForegroundColor White }
7 function Write-Ok($msg) { Write-Host "[ok] $msg" -ForegroundColor Green }
8 function Write-Warn($msg) { Write-Host "[warn] $msg" -ForegroundColor Yellow }
9 function Write-Err($msg) { Write-Host "[error] $msg" -ForegroundColor Red }
10 function Write-Info($msg) { Write-Host "[info] $msg" -ForegroundColor Blue }
11
12 function Test-Command($cmd) {
13 try { Get-Command $cmd -ErrorAction Stop | Out-Null; return $true }
14 catch { return $false }
15 }
16
17 # --- Header ------------------------------------------------------------------
18
19 Write-Host "`nPlanOpticon Installer" -ForegroundColor White
20 Write-Host "=====================================" -ForegroundColor White
21
22 # --- Python ------------------------------------------------------------------
23
24 Write-Step "Checking Python"
25
26 $python = $null
27 foreach ($cmd in @("python", "python3", "py")) {
28 if (Test-Command $cmd) {
29 $ver = & $cmd -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>$null
30 if ($ver) {
31 $parts = $ver.Split(".")
32 if ([int]$parts[0] -ge 3 -and [int]$parts[1] -ge 10) {
33 $python = $cmd
34 Write-Ok "Python $ver found ($cmd)"
35 break
36 }
37 }
38 }
39 }
40
41 if (-not $python) {
42 Write-Warn "Python 3.10+ not found"
43 if (Test-Command "winget") {
44 Write-Info "Installing Python via winget..."
45 winget install Python.Python.3.12 --accept-source-agreements --accept-package-agreements
46 $python = "python"
47 } elseif (Test-Command "choco") {
48 Write-Info "Installing Python via Chocolatey..."
49 choco install python312 -y
50 $python = "python"
51 } else {
52 Write-Err "Please install Python 3.10+ from https://www.python.org/downloads/"
53 exit 1
54 }
55 # Refresh PATH
56 $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
57 Write-Ok "Python installed"
58 }
59
60 # --- FFmpeg ------------------------------------------------------------------
61
62 Write-Step "Checking FFmpeg"
63
64 if (Test-Command "ffmpeg") {
65 Write-Ok "FFmpeg found"
66 } else {
67 Write-Warn "FFmpeg not found"
68 if (Test-Command "winget") {
69 Write-Info "Installing FFmpeg via winget..."
70 winget install Gyan.FFmpeg --accept-source-agreements --accept-package-agreements
71 } elseif (Test-Command "choco") {
72 Write-Info "Installing FFmpeg via Chocolatey..."
73 choco install ffmpeg -y
74 } else {
75 Write-Err "Please install FFmpeg from https://ffmpeg.org/download.html"
76 exit 1
77 }
78 $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
79 Write-Ok "FFmpeg installed"
80 }
81
82 # --- Extras ------------------------------------------------------------------
83
84 Write-Step "Choose extras"
85 Write-Host " 1) core - just the basics (default)"
86 Write-Host " 2) cloud - Google Drive, Dropbox, S3"
87 Write-Host " 3) pdf - PDF document ingestion"
88 Write-Host " 4) sources - YouTube, RSS, web scraping"
89 Write-Host " 5) all - everything"
90 Write-Host ""
91
92 $choice = Read-Host "Choose extras [1-5, default=1]"
93 $extras = switch ($choice) {
94 "2" { "[cloud]" }
95 "3" { "[pdf]" }
96 "4" { "[sources]" }
97 "5" { "[all]" }
98 default { "" }
99 }
100
101 # --- Install PlanOpticon -----------------------------------------------------
102
103 Write-Step "Installing PlanOpticon"
104
105 $target = "planopticon$extras"
106 Write-Info "Installing: $target"
107
108 try {
109 & $python -m pip install --upgrade $target
110 Write-Ok "PlanOpticon installed"
111 } catch {
112 Write-Warn "pip install failed, trying with --user"
113 & $python -m pip install --user --upgrade $target
114 Write-Ok "PlanOpticon installed (user)"
115 }
116
117 # --- API key setup -----------------------------------------------------------
118
119 Write-Step "API key setup"
120
121 $envFile = ".env"
122 if (Test-Path $envFile) {
123 Write-Ok "Found existing .env file"
124 } else {
125 Write-Host "`nPlanOpticon needs at least one AI provider API key."
126 Write-Host " 1) OpenAI (OPENAI_API_KEY)"
127 Write-Host " 2) Anthropic (ANTHROPIC_API_KEY)"
128 Write-Host " 3) Google (GEMINI_API_KEY)"
129 Write-Host " 4) Ollama (local, no key needed)"
130 Write-Host " 5) Skip for now"
131 Write-Host ""
132
133 $providerChoice = Read-Host "Choose provider [1-5]"
134 switch ($providerChoice) {
135 "1" {
136 $key = Read-Host "Enter your OpenAI API key"
137 if ($key) { "OPENAI_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
138 }
139 "2" {
140 $key = Read-Host "Enter your Anthropic API key"
141 if ($key) { "ANTHROPIC_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
142 }
143 "3" {
144 $key = Read-Host "Enter your Google/Gemini API key"
145 if ($key) { "GEMINI_API_KEY=$key" | Out-File $envFile -Encoding utf8; Write-Ok "Saved to .env" }
146 }
147 "4" {
148 "OLLAMA_HOST=http://localhost:11434" | Out-File $envFile -Encoding utf8
149 Write-Info "Using Ollama - no API key needed"
150 }
151 default { Write-Warn "Skipping API key setup. Add keys to .env later." }
152 }
153 }
154
155 # --- Verify ------------------------------------------------------------------
156
157 Write-Step "Verifying installation"
158
159 if (Test-Command "planopticon") {
160 try {
161 $version = & planopticon --version 2>$null
162 Write-Ok "planopticon CLI ready ($version)"
163 } catch {
164 Write-Ok "planopticon CLI ready"
165 }
166 } else {
167 Write-Warn "planopticon not in PATH - restart your terminal"
168 }
169
170 # --- Done --------------------------------------------------------------------
171
172 Write-Host "`nInstallation complete!" -ForegroundColor Green
173 Write-Host ""
174 Write-Host "Quick start:"
175 Write-Host " planopticon process video.mp4 # Analyze a video"
176 Write-Host " planopticon companion # Start AI companion"
177 Write-Host " planopticon list-models # Check available models"
178 Write-Host ""
179 Write-Host "Docs: https://planopticon.dev"
180 Write-Host ""
+375
--- a/install.sh
+++ b/install.sh
@@ -0,0 +1,375 @@
1
+#!/usr/bin/env bash
2
+# PlanOpticon installer — cross-platform one-command setup
3
+# Usage: curl -fsSL https://planopticon.dev/install.sh | bash
4
+#
5
+# Supports: macOS (Homebrew), Ubuntu/Debian (apt), Fedora/RHEL (dnf), Arch (pacman)
6
+# Idempotent — safe to re-run.
7
+
8
+set -euo pipefail
9
+
10
+# --- Colors & helpers --------------------------------------------------------
11
+
12
+RED='\033[0;31m'
13
+GREEN='\033[0;32m'
14
+YELLOW='\033[1;33m'
15
+BLUE='\033[0;34m'
16
+BOLD='\033[1m'
17
+NC='\033[0m'
18
+
19
+info() { printf "${BLUE}[info]${NC} %s\n" "$*"; }
20
+ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; }
21
+warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; }
22
+err() { printf "${RED}[error]${NC} %s\n" "$*" >&2; }
23
+step() { printf "\n${BOLD}==> %s${NC}\n" "$*"; }
24
+
25
+command_exists() { command -v "$1" >/dev/null 2>&1; }
26
+
27
+# --- Detect OS & package manager ---------------------------------------------
28
+
29
+detect_os() {
30
+ case "$(uname -s)" in
31
+ Darwin) OS="macos" ;;
32
+ Linux)
33
+ if [ -f /etc/os-release ]; then
34
+ . /etc/os-release
35
+ case "$ID" in
36
+ ubuntu|debian|pop|linuxmint|elementary) OS="debian" ;;
37
+ fedora|rhel|centos|rocky|alma) OS="fedora" ;;
38
+ arch|manjaro|endeavouros) OS="arch" ;;
39
+ *) OS="linux-unknown" ;;
40
+ esac
41
+ else
42
+ OS="linux-unknown"
43
+ fi
44
+ ;;
45
+ MINGW*|MSYS*|CYGWIN*)
46
+ err "Windows detected. Please use install.ps1 instead:"
47
+ err " irm https://planopticon.dev/install.ps1 | iex"
48
+ exit 1
49
+ ;;
50
+ *)
51
+ err "Unsupported OS: $(uname -s)"
52
+ exit 1
53
+ ;;
54
+ esac
55
+}
56
+
57
+# --- Python ------------------------------------------------------------------
58
+
59
+MIN_PYTHON_MAJOR=3
60
+MIN_PYTHON_MINOR=10
61
+
62
+find_python() {
63
+ for cmd in python3 python; do
64
+ if command_exists "$cmd"; then
65
+ local ver
66
+ ver=$("$cmd" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null) || continue
67
+ local major minor
68
+ major=$(echo "$ver" | cut -d. -f1)
69
+ minor=$(echo "$ver" | cut -d. -f2)
70
+ if [ "$major" -ge "$MIN_PYTHON_MAJOR" ] && [ "$minor" -ge "$MIN_PYTHON_MINOR" ]; then
71
+ PYTHON="$cmd"
72
+ PYTHON_VERSION="$ver"
73
+ return 0
74
+ fi
75
+ fi
76
+ done
77
+ return 1
78
+}
79
+
80
+install_python() {
81
+ step "Installing Python 3.10+"
82
+ case "$OS" in
83
+ macos)
84
+ if command_exists brew; then
85
+ brew install [email protected]
86
+ else
87
+ err "Homebrew not found. Install it first: https://brew.sh"
88
+ exit 1
89
+ fi
90
+ ;;
91
+ debian)
92
+ sudo apt-get update -qq
93
+ sudo apt-get install -y -qq python3 python3-pip python3-venv
94
+ ;;
95
+ fedora)
96
+ sudo dnf install -y -q python3 python3-pip
97
+ ;;
98
+ arch)
99
+ sudo pacman -S --noconfirm python python-pip
100
+ ;;
101
+ *)
102
+ err "Cannot auto-install Python on this OS."
103
+ err "Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually."
104
+ exit 1
105
+ ;;
106
+ esac
107
+}
108
+
109
+# --- FFmpeg ------------------------------------------------------------------
110
+
111
+install_ffmpeg() {
112
+ step "Installing FFmpeg"
113
+ case "$OS" in
114
+ macos)
115
+ if command_exists brew; then
116
+ brew install ffmpeg
117
+ else
118
+ err "Homebrew not found. Install it first: https://brew.sh"
119
+ exit 1
120
+ fi
121
+ ;;
122
+ debian)
123
+ sudo apt-get update -qq
124
+ sudo apt-get install -y -qq ffmpeg
125
+ ;;
126
+ fedora)
127
+ sudo dnf install -y -q ffmpeg-free || {
128
+ warn "ffmpeg-free not available, trying RPM Fusion..."
129
+ sudo dnf install -y -q \
130
+ "https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" 2>/dev/null || true
131
+ sudo dnf install -y -q ffmpeg
132
+ }
133
+ ;;
134
+ arch)
135
+ sudo pacman -S --noconfirm ffmpeg
136
+ ;;
137
+ *)
138
+ err "Cannot auto-install FFmpeg on this OS."
139
+ err "Please install FFmpeg manually: https://ffmpeg.org/download.html"
140
+ exit 1
141
+ ;;
142
+ esac
143
+}
144
+
145
+# --- Ollama (optional) -------------------------------------------------------
146
+
147
+install_ollama() {
148
+ step "Installing Ollama"
149
+ if command_exists ollama; then
150
+ ok "Ollama already installed"
151
+ return 0
152
+ fi
153
+ case "$OS" in
154
+ macos)
155
+ if command_exists brew; then
156
+ brew install ollama
157
+ else
158
+ curl -fsSL https://ollama.com/install.sh | sh
159
+ fi
160
+ ;;
161
+ debian|fedora|arch|linux-unknown)
162
+ curl -fsSL https://ollama.com/install.sh | sh
163
+ ;;
164
+ esac
165
+}
166
+
167
+pull_ollama_models() {
168
+ info "Pulling recommended local models..."
169
+ ollama pull llama3.2 2>/dev/null || warn "Failed to pull llama3.2"
170
+ ollama pull llava 2>/dev/null || warn "Failed to pull llava (vision model)"
171
+ ok "Ollama models ready"
172
+}
173
+
174
+# --- Extras selection --------------------------------------------------------
175
+
176
+select_extras() {
177
+ EXTRAS=""
178
+ printf "\n${BOLD}Optional extras:${NC}\n"
179
+ echo " 1) core — just the basics (default)"
180
+ echo " 2) cloud — Google Drive, Dropbox, S3 integrations"
181
+ echo " 3) pdf — PDF document ingestion"
182
+ echo " 4) sources — YouTube, RSS, web scraping"
183
+ echo " 5) all — everything"
184
+ echo ""
185
+ printf "Choose extras [1-5, default=1]: "
186
+
187
+ if [ -t 0 ]; then
188
+ read -r choice
189
+ else
190
+ choice="1"
191
+ echo "1 (non-interactive, using default)"
192
+ fi
193
+
194
+ case "${choice:-1}" in
195
+ 2) EXTRAS="[cloud]" ;;
196
+ 3) EXTRAS="[pdf]" ;;
197
+ 4) EXTRAS="[sources]" ;;
198
+ 5) EXTRAS="[all]" ;;
199
+ *) EXTRAS="" ;;
200
+ esac
201
+}
202
+
203
+# --- API key setup -----------------------------------------------------------
204
+
205
+setup_api_keys() {
206
+ step "API key setup"
207
+ local env_file=".env"
208
+
209
+ if [ -f "$env_file" ]; then
210
+ ok "Found existing .env file, skipping API key setup"
211
+ return 0
212
+ fi
213
+
214
+ if [ ! -t 0 ]; then
215
+ warn "Non-interactive mode — skipping API key setup"
216
+ info "Create a .env file with at least one API key:"
217
+ info " OPENAI_API_KEY=sk-..."
218
+ info " ANTHROPIC_API_KEY=sk-ant-..."
219
+ info " GEMINI_API_KEY=..."
220
+ return 0
221
+ fi
222
+
223
+ printf "\n${BOLD}PlanOpticon needs at least one AI provider API key.${NC}\n"
224
+ echo "You can add more later in .env"
225
+ echo ""
226
+ echo " 1) OpenAI (OPENAI_API_KEY)"
227
+ echo " 2) Anthropic (ANTHROPIC_API_KEY)"
228
+ echo " 3) Google (GEMINI_API_KEY)"
229
+ echo " 4) Ollama (local, no key needed)"
230
+ echo " 5) Skip for now"
231
+ echo ""
232
+ printf "Choose provider [1-5]: "
233
+ read -r provider_choice
234
+
235
+ case "${provider_choice:-5}" in
236
+ 1)
237
+ printf "Enter your OpenAI API key: "
238
+ read -r api_key
239
+ if [ -n "$api_key" ]; then
240
+ echo "OPENAI_API_KEY=$api_key" > "$env_file"
241
+ ok "Saved to .env"
242
+ fi
243
+ ;;
244
+ 2)
245
+ printf "Enter your Anthropic API key: "
246
+ read -r api_key
247
+ if [ -n "$api_key" ]; then
248
+ echo "ANTHROPIC_API_KEY=$api_key" > "$env_file"
249
+ ok "Saved to .env"
250
+ fi
251
+ ;;
252
+ 3)
253
+ printf "Enter your Google/Gemini API key: "
254
+ read -r api_key
255
+ if [ -n "$api_key" ]; then
256
+ echo "GEMINI_API_KEY=$api_key" > "$env_file"
257
+ ok "Saved to .env"
258
+ fi
259
+ ;;
260
+ 4)
261
+ info "Using Ollama — no API key needed"
262
+ echo "OLLAMA_HOST=http://localhost:11434" > "$env_file"
263
+ ;;
264
+ 5)
265
+ warn "Skipping API key setup. Add keys to .env later."
266
+ ;;
267
+ esac
268
+}
269
+
270
+# --- Main --------------------------------------------------------------------
271
+
272
+main() {
273
+ printf "\n${BOLD}PlanOpticon Installer${NC}\n"
274
+ echo "====================================="
275
+ echo ""
276
+
277
+ detect_os
278
+ info "Detected OS: $OS"
279
+
280
+ # --- Python ---
281
+ step "Checking Python"
282
+ if find_python; then
283
+ ok "Python $PYTHON_VERSION found ($PYTHON)"
284
+ else
285
+ warn "Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ not found"
286
+ install_python
287
+ if ! find_python; then
288
+ err "Python installation failed. Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually."
289
+ exit 1
290
+ fi
291
+ ok "Python $PYTHON_VERSION installed"
292
+ fi
293
+
294
+ # --- FFmpeg ---
295
+ step "Checking FFmpeg"
296
+ if command_exists ffmpeg; then
297
+ local ffmpeg_ver
298
+ ffmpeg_ver=$(ffmpeg -version 2>/dev/null | head -1 | awk '{print $3}') || ffmpeg_ver="unknown"
299
+ ok "FFmpeg found ($ffmpeg_ver)"
300
+ else
301
+ warn "FFmpeg not found"
302
+ install_ffmpeg
303
+ if command_exists ffmpeg; then
304
+ ok "FFmpeg installed"
305
+ else
306
+ err "FFmpeg installation failed. Please install manually."
307
+ exit 1
308
+ fi
309
+ fi
310
+
311
+ # --- pip install planopticon ---
312
+ step "Installing PlanOpticon"
313
+ select_extras
314
+
315
+ local pip_target="planopticon${EXTRAS}"
316
+ info "Installing: $pip_target"
317
+
318
+ if "$PYTHON" -m pip install --upgrade "$pip_target" 2>&1; then
319
+ ok "PlanOpticon installed"
320
+ else
321
+ warn "pip install failed, trying with --user flag"
322
+ "$PYTHON" -m pip install --user --upgrade "$pip_target"
323
+ ok "PlanOpticon installed (user)"
324
+ fi
325
+
326
+ # --- Ollama (optional) ---
327
+ if [ -t 0 ]; then
328
+ printf "\n${BOLD}Would you like to install Ollama for local AI? [y/N]: ${NC}"
329
+ read -r install_ollama_choice
330
+ if [[ "${install_ollama_choice:-n}" =~ ^[Yy] ]]; then
331
+ install_ollama
332
+ if command_exists ollama; then
333
+ printf "Pull recommended models? (llama3.2 + llava, ~6GB) [y/N]: "
334
+ read -r pull_choice
335
+ if [[ "${pull_choice:-n}" =~ ^[Yy] ]]; then
336
+ pull_ollama_models
337
+ fi
338
+ fi
339
+ fi
340
+ fi
341
+
342
+ # --- API keys ---
343
+ setup_api_keys
344
+
345
+ # --- Verify ---
346
+ step "Verifying installation"
347
+ if command_exists planopticon; then
348
+ local version
349
+ version=$(planopticon --version 2>/dev/null || echo "installed")
350
+ ok "planopticon CLI ready ($version)"
351
+ else
352
+ # Might need PATH update
353
+ warn "planopticon not in PATH — you may need to restart your shell"
354
+ info "Or run: $PYTHON -m video_processor.cli.commands --version"
355
+ fi
356
+
357
+ if planopticon list-models >/dev/null 2>&1; then
358
+ ok "Provider check passed"
359
+ else
360
+ warn "No AI providers configured yet — add an API key to .env"
361
+ fi
362
+
363
+ # --- Done ---
364
+ printf "\n${GREEN}${BOLD}Installation complete!${NC}\n"
365
+ echo ""
366
+ echo "Quick start:"
367
+ echo " planopticon process video.mp4 # Analyze a video"
368
+ echo " planopticon companion # Start AI companion"
369
+ echo " planopticon list-models # Check available models"
370
+ echo ""
371
+ echo "Docs: https://planopticon.dev"
372
+ echo ""
373
+}
374
+
375
+main "$@"
--- a/install.sh
+++ b/install.sh
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/install.sh
+++ b/install.sh
@@ -0,0 +1,375 @@
1 #!/usr/bin/env bash
2 # PlanOpticon installer — cross-platform one-command setup
3 # Usage: curl -fsSL https://planopticon.dev/install.sh | bash
4 #
5 # Supports: macOS (Homebrew), Ubuntu/Debian (apt), Fedora/RHEL (dnf), Arch (pacman)
6 # Idempotent — safe to re-run.
7
8 set -euo pipefail
9
10 # --- Colors & helpers --------------------------------------------------------
11
12 RED='\033[0;31m'
13 GREEN='\033[0;32m'
14 YELLOW='\033[1;33m'
15 BLUE='\033[0;34m'
16 BOLD='\033[1m'
17 NC='\033[0m'
18
19 info() { printf "${BLUE}[info]${NC} %s\n" "$*"; }
20 ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; }
21 warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; }
22 err() { printf "${RED}[error]${NC} %s\n" "$*" >&2; }
23 step() { printf "\n${BOLD}==> %s${NC}\n" "$*"; }
24
25 command_exists() { command -v "$1" >/dev/null 2>&1; }
26
27 # --- Detect OS & package manager ---------------------------------------------
28
29 detect_os() {
30 case "$(uname -s)" in
31 Darwin) OS="macos" ;;
32 Linux)
33 if [ -f /etc/os-release ]; then
34 . /etc/os-release
35 case "$ID" in
36 ubuntu|debian|pop|linuxmint|elementary) OS="debian" ;;
37 fedora|rhel|centos|rocky|alma) OS="fedora" ;;
38 arch|manjaro|endeavouros) OS="arch" ;;
39 *) OS="linux-unknown" ;;
40 esac
41 else
42 OS="linux-unknown"
43 fi
44 ;;
45 MINGW*|MSYS*|CYGWIN*)
46 err "Windows detected. Please use install.ps1 instead:"
47 err " irm https://planopticon.dev/install.ps1 | iex"
48 exit 1
49 ;;
50 *)
51 err "Unsupported OS: $(uname -s)"
52 exit 1
53 ;;
54 esac
55 }
56
57 # --- Python ------------------------------------------------------------------
58
59 MIN_PYTHON_MAJOR=3
60 MIN_PYTHON_MINOR=10
61
62 find_python() {
63 for cmd in python3 python; do
64 if command_exists "$cmd"; then
65 local ver
66 ver=$("$cmd" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>/dev/null) || continue
67 local major minor
68 major=$(echo "$ver" | cut -d. -f1)
69 minor=$(echo "$ver" | cut -d. -f2)
70 if [ "$major" -ge "$MIN_PYTHON_MAJOR" ] && [ "$minor" -ge "$MIN_PYTHON_MINOR" ]; then
71 PYTHON="$cmd"
72 PYTHON_VERSION="$ver"
73 return 0
74 fi
75 fi
76 done
77 return 1
78 }
79
80 install_python() {
81 step "Installing Python 3.10+"
82 case "$OS" in
83 macos)
84 if command_exists brew; then
85 brew install [email protected]
86 else
87 err "Homebrew not found. Install it first: https://brew.sh"
88 exit 1
89 fi
90 ;;
91 debian)
92 sudo apt-get update -qq
93 sudo apt-get install -y -qq python3 python3-pip python3-venv
94 ;;
95 fedora)
96 sudo dnf install -y -q python3 python3-pip
97 ;;
98 arch)
99 sudo pacman -S --noconfirm python python-pip
100 ;;
101 *)
102 err "Cannot auto-install Python on this OS."
103 err "Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually."
104 exit 1
105 ;;
106 esac
107 }
108
109 # --- FFmpeg ------------------------------------------------------------------
110
111 install_ffmpeg() {
112 step "Installing FFmpeg"
113 case "$OS" in
114 macos)
115 if command_exists brew; then
116 brew install ffmpeg
117 else
118 err "Homebrew not found. Install it first: https://brew.sh"
119 exit 1
120 fi
121 ;;
122 debian)
123 sudo apt-get update -qq
124 sudo apt-get install -y -qq ffmpeg
125 ;;
126 fedora)
127 sudo dnf install -y -q ffmpeg-free || {
128 warn "ffmpeg-free not available, trying RPM Fusion..."
129 sudo dnf install -y -q \
130 "https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" 2>/dev/null || true
131 sudo dnf install -y -q ffmpeg
132 }
133 ;;
134 arch)
135 sudo pacman -S --noconfirm ffmpeg
136 ;;
137 *)
138 err "Cannot auto-install FFmpeg on this OS."
139 err "Please install FFmpeg manually: https://ffmpeg.org/download.html"
140 exit 1
141 ;;
142 esac
143 }
144
145 # --- Ollama (optional) -------------------------------------------------------
146
147 install_ollama() {
148 step "Installing Ollama"
149 if command_exists ollama; then
150 ok "Ollama already installed"
151 return 0
152 fi
153 case "$OS" in
154 macos)
155 if command_exists brew; then
156 brew install ollama
157 else
158 curl -fsSL https://ollama.com/install.sh | sh
159 fi
160 ;;
161 debian|fedora|arch|linux-unknown)
162 curl -fsSL https://ollama.com/install.sh | sh
163 ;;
164 esac
165 }
166
167 pull_ollama_models() {
168 info "Pulling recommended local models..."
169 ollama pull llama3.2 2>/dev/null || warn "Failed to pull llama3.2"
170 ollama pull llava 2>/dev/null || warn "Failed to pull llava (vision model)"
171 ok "Ollama models ready"
172 }
173
174 # --- Extras selection --------------------------------------------------------
175
176 select_extras() {
177 EXTRAS=""
178 printf "\n${BOLD}Optional extras:${NC}\n"
179 echo " 1) core — just the basics (default)"
180 echo " 2) cloud — Google Drive, Dropbox, S3 integrations"
181 echo " 3) pdf — PDF document ingestion"
182 echo " 4) sources — YouTube, RSS, web scraping"
183 echo " 5) all — everything"
184 echo ""
185 printf "Choose extras [1-5, default=1]: "
186
187 if [ -t 0 ]; then
188 read -r choice
189 else
190 choice="1"
191 echo "1 (non-interactive, using default)"
192 fi
193
194 case "${choice:-1}" in
195 2) EXTRAS="[cloud]" ;;
196 3) EXTRAS="[pdf]" ;;
197 4) EXTRAS="[sources]" ;;
198 5) EXTRAS="[all]" ;;
199 *) EXTRAS="" ;;
200 esac
201 }
202
203 # --- API key setup -----------------------------------------------------------
204
205 setup_api_keys() {
206 step "API key setup"
207 local env_file=".env"
208
209 if [ -f "$env_file" ]; then
210 ok "Found existing .env file, skipping API key setup"
211 return 0
212 fi
213
214 if [ ! -t 0 ]; then
215 warn "Non-interactive mode — skipping API key setup"
216 info "Create a .env file with at least one API key:"
217 info " OPENAI_API_KEY=sk-..."
218 info " ANTHROPIC_API_KEY=sk-ant-..."
219 info " GEMINI_API_KEY=..."
220 return 0
221 fi
222
223 printf "\n${BOLD}PlanOpticon needs at least one AI provider API key.${NC}\n"
224 echo "You can add more later in .env"
225 echo ""
226 echo " 1) OpenAI (OPENAI_API_KEY)"
227 echo " 2) Anthropic (ANTHROPIC_API_KEY)"
228 echo " 3) Google (GEMINI_API_KEY)"
229 echo " 4) Ollama (local, no key needed)"
230 echo " 5) Skip for now"
231 echo ""
232 printf "Choose provider [1-5]: "
233 read -r provider_choice
234
235 case "${provider_choice:-5}" in
236 1)
237 printf "Enter your OpenAI API key: "
238 read -r api_key
239 if [ -n "$api_key" ]; then
240 echo "OPENAI_API_KEY=$api_key" > "$env_file"
241 ok "Saved to .env"
242 fi
243 ;;
244 2)
245 printf "Enter your Anthropic API key: "
246 read -r api_key
247 if [ -n "$api_key" ]; then
248 echo "ANTHROPIC_API_KEY=$api_key" > "$env_file"
249 ok "Saved to .env"
250 fi
251 ;;
252 3)
253 printf "Enter your Google/Gemini API key: "
254 read -r api_key
255 if [ -n "$api_key" ]; then
256 echo "GEMINI_API_KEY=$api_key" > "$env_file"
257 ok "Saved to .env"
258 fi
259 ;;
260 4)
261 info "Using Ollama — no API key needed"
262 echo "OLLAMA_HOST=http://localhost:11434" > "$env_file"
263 ;;
264 5)
265 warn "Skipping API key setup. Add keys to .env later."
266 ;;
267 esac
268 }
269
270 # --- Main --------------------------------------------------------------------
271
272 main() {
273 printf "\n${BOLD}PlanOpticon Installer${NC}\n"
274 echo "====================================="
275 echo ""
276
277 detect_os
278 info "Detected OS: $OS"
279
280 # --- Python ---
281 step "Checking Python"
282 if find_python; then
283 ok "Python $PYTHON_VERSION found ($PYTHON)"
284 else
285 warn "Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ not found"
286 install_python
287 if ! find_python; then
288 err "Python installation failed. Please install Python ${MIN_PYTHON_MAJOR}.${MIN_PYTHON_MINOR}+ manually."
289 exit 1
290 fi
291 ok "Python $PYTHON_VERSION installed"
292 fi
293
294 # --- FFmpeg ---
295 step "Checking FFmpeg"
296 if command_exists ffmpeg; then
297 local ffmpeg_ver
298 ffmpeg_ver=$(ffmpeg -version 2>/dev/null | head -1 | awk '{print $3}') || ffmpeg_ver="unknown"
299 ok "FFmpeg found ($ffmpeg_ver)"
300 else
301 warn "FFmpeg not found"
302 install_ffmpeg
303 if command_exists ffmpeg; then
304 ok "FFmpeg installed"
305 else
306 err "FFmpeg installation failed. Please install manually."
307 exit 1
308 fi
309 fi
310
311 # --- pip install planopticon ---
312 step "Installing PlanOpticon"
313 select_extras
314
315 local pip_target="planopticon${EXTRAS}"
316 info "Installing: $pip_target"
317
318 if "$PYTHON" -m pip install --upgrade "$pip_target" 2>&1; then
319 ok "PlanOpticon installed"
320 else
321 warn "pip install failed, trying with --user flag"
322 "$PYTHON" -m pip install --user --upgrade "$pip_target"
323 ok "PlanOpticon installed (user)"
324 fi
325
326 # --- Ollama (optional) ---
327 if [ -t 0 ]; then
328 printf "\n${BOLD}Would you like to install Ollama for local AI? [y/N]: ${NC}"
329 read -r install_ollama_choice
330 if [[ "${install_ollama_choice:-n}" =~ ^[Yy] ]]; then
331 install_ollama
332 if command_exists ollama; then
333 printf "Pull recommended models? (llama3.2 + llava, ~6GB) [y/N]: "
334 read -r pull_choice
335 if [[ "${pull_choice:-n}" =~ ^[Yy] ]]; then
336 pull_ollama_models
337 fi
338 fi
339 fi
340 fi
341
342 # --- API keys ---
343 setup_api_keys
344
345 # --- Verify ---
346 step "Verifying installation"
347 if command_exists planopticon; then
348 local version
349 version=$(planopticon --version 2>/dev/null || echo "installed")
350 ok "planopticon CLI ready ($version)"
351 else
352 # Might need PATH update
353 warn "planopticon not in PATH — you may need to restart your shell"
354 info "Or run: $PYTHON -m video_processor.cli.commands --version"
355 fi
356
357 if planopticon list-models >/dev/null 2>&1; then
358 ok "Provider check passed"
359 else
360 warn "No AI providers configured yet — add an API key to .env"
361 fi
362
363 # --- Done ---
364 printf "\n${GREEN}${BOLD}Installation complete!${NC}\n"
365 echo ""
366 echo "Quick start:"
367 echo " planopticon process video.mp4 # Analyze a video"
368 echo " planopticon companion # Start AI companion"
369 echo " planopticon list-models # Check available models"
370 echo ""
371 echo "Docs: https://planopticon.dev"
372 echo ""
373 }
374
375 main "$@"

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button