@darkrei08/setup-ai 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/setup-ai.mjs +1 -0
- package/package.json +1 -1
- package/setup-ai.ps1 +115 -10
- package/setup-ai.sh +92 -8
package/bin/setup-ai.mjs
CHANGED
|
@@ -44,6 +44,7 @@ const MODULES = [
|
|
|
44
44
|
{ name: "antigravity", core: true, desc: "Google Antigravity CLI (agy)" },
|
|
45
45
|
{ name: "opencode", core: true, desc: "opencode agent CLI (opencode-ai)" },
|
|
46
46
|
{ name: "cockpit", core: false, desc: "cockpit-tools desktop GUI app (optional, CC BY-NC-SA)" },
|
|
47
|
+
{ name: "rotator", core: false, desc: "tuxevil-rotator multi-account Gemini/Antigravity gateway (optional, opt-in)" },
|
|
47
48
|
];
|
|
48
49
|
|
|
49
50
|
// dotenv is Linux-only; drop it from the Windows menu.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darkrei08/setup-ai",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Cross-OS installer for an AI coding toolchain (pi, codex, antigravity, opencode, herdr, gentle-ai/gga, Engineering Excellence) with an interactive module menu and per-agent MCP registration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/setup-ai.ps1
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<#
|
|
3
3
|
==============================================================================
|
|
4
4
|
AI Dev Suite — Engineering Excellence Edition (Windows)
|
|
5
|
-
Version: 3.
|
|
5
|
+
Version: 3.3.0
|
|
6
6
|
|
|
7
7
|
Windows-native installer, sibling of setup-ai.sh. Uses each tool's official
|
|
8
8
|
Windows method: winget for language runtimes, the vendor install.ps1 scripts
|
|
@@ -50,7 +50,7 @@ try {
|
|
|
50
50
|
Write-Warning "Could not set UTF-8 console encoding: $($_.Exception.Message)"
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
$ScriptVersion = "3.
|
|
53
|
+
$ScriptVersion = "3.3.0"
|
|
54
54
|
$ScriptPath = $PSCommandPath
|
|
55
55
|
$ScriptDir = Split-Path -Parent $ScriptPath
|
|
56
56
|
$LogDir = Join-Path $ScriptDir "logs"
|
|
@@ -94,6 +94,19 @@ $SkillAgentRoots = @{
|
|
|
94
94
|
codex = Join-Path $HOME ".codex\skills"
|
|
95
95
|
opencode = Join-Path $HOME ".config\opencode\skills"
|
|
96
96
|
}
|
|
97
|
+
# Candidate skill roots per agent: verification passes if SKILL.md exists in any
|
|
98
|
+
# of them. Every agent keeps its single existing root; Codex also accepts
|
|
99
|
+
# ~/.agents/skills because upstream `skills add --global` writes Codex skills
|
|
100
|
+
# there instead of ~/.codex/skills.
|
|
101
|
+
$SkillAgentCandidateRoots = @{
|
|
102
|
+
pi = @($SkillAgentRoots['pi'])
|
|
103
|
+
'claude-code' = @($SkillAgentRoots['claude-code'])
|
|
104
|
+
'gemini-cli' = @($SkillAgentRoots['gemini-cli'])
|
|
105
|
+
cursor = @($SkillAgentRoots['cursor'])
|
|
106
|
+
antigravity = @($SkillAgentRoots['antigravity'])
|
|
107
|
+
codex = @($SkillAgentRoots['codex'], (Join-Path $HOME ".agents\skills"))
|
|
108
|
+
opencode = @($SkillAgentRoots['opencode'])
|
|
109
|
+
}
|
|
97
110
|
|
|
98
111
|
# ------------------------------------------------------------------------------
|
|
99
112
|
# Logging (human + JSONL)
|
|
@@ -135,18 +148,32 @@ function Update-SessionPath {
|
|
|
135
148
|
|
|
136
149
|
# Run a step; $Optional means failures are logged as WARN and swallowed.
|
|
137
150
|
function Invoke-Step {
|
|
138
|
-
param(
|
|
151
|
+
param(
|
|
152
|
+
[string]$Phase,
|
|
153
|
+
[scriptblock]$Action,
|
|
154
|
+
[switch]$Optional,
|
|
155
|
+
[int[]]$ExpectedExitCodes = @()
|
|
156
|
+
)
|
|
139
157
|
Write-Log INFO $Phase "step_start" "Running step"
|
|
140
158
|
try {
|
|
141
159
|
$global:LASTEXITCODE = 0
|
|
142
160
|
& $Action 2>&1 | Tee-Object -FilePath $HumanLog -Append | Out-Host
|
|
143
161
|
$nativeExitCode = $global:LASTEXITCODE
|
|
144
162
|
if ($nativeExitCode -ne 0) {
|
|
163
|
+
if ($ExpectedExitCodes -contains $nativeExitCode) {
|
|
164
|
+
Write-Log INFO $Phase "step_expected" "Step returned expected exit code $nativeExitCode" $nativeExitCode
|
|
165
|
+
return $null
|
|
166
|
+
}
|
|
145
167
|
throw "Native command exited with code $nativeExitCode"
|
|
146
168
|
}
|
|
147
169
|
Write-Log INFO $Phase "step_ok" "Step completed"
|
|
148
170
|
return $true
|
|
149
171
|
} catch {
|
|
172
|
+
$nativeExitCode = $global:LASTEXITCODE
|
|
173
|
+
if ($ExpectedExitCodes -contains $nativeExitCode) {
|
|
174
|
+
Write-Log INFO $Phase "step_expected" "Step returned expected exit code $nativeExitCode" $nativeExitCode
|
|
175
|
+
return $null
|
|
176
|
+
}
|
|
150
177
|
if ($Optional) {
|
|
151
178
|
Write-Log WARN $Phase "step_failed_optional" "$($_.Exception.Message); continuing" 1
|
|
152
179
|
return $false
|
|
@@ -159,13 +186,18 @@ function Invoke-Step {
|
|
|
159
186
|
function Test-WingetInstalled {
|
|
160
187
|
param([string]$Id, [string]$Phase)
|
|
161
188
|
$probe = Join-Path ([IO.Path]::GetTempPath()) ("setup-ai-winget-" + [guid]::NewGuid().ToString("N") + ".log")
|
|
189
|
+
$wingetNoApplicationsFoundExitCode = -1978335212 # 0x8A150014: package is not installed.
|
|
162
190
|
try {
|
|
163
191
|
# Keep the query inside Invoke-Step so its exit status and output are
|
|
164
192
|
# logged; a failed probe is treated as "not installed" and followed
|
|
165
193
|
# by the mandatory install step.
|
|
166
|
-
$listed = Invoke-Step -Phase $Phase -Optional -Action {
|
|
194
|
+
$listed = Invoke-Step -Phase $Phase -Optional -ExpectedExitCodes $wingetNoApplicationsFoundExitCode -Action {
|
|
167
195
|
winget list --id $Id -e | Out-File -LiteralPath $probe -Encoding utf8
|
|
168
196
|
}
|
|
197
|
+
if ($null -eq $listed) {
|
|
198
|
+
Write-Log INFO $Phase "not_installed" "$Id is not installed; will install"
|
|
199
|
+
return $false
|
|
200
|
+
}
|
|
169
201
|
return ($listed -and [bool](Select-String -Path $probe -SimpleMatch $Id -Quiet))
|
|
170
202
|
} finally {
|
|
171
203
|
if (Test-Path $probe) {
|
|
@@ -263,9 +295,9 @@ function Get-TargetSkillAgents {
|
|
|
263
295
|
function Assert-SkillInstalledForAgents {
|
|
264
296
|
param([string]$Phase, [string]$Skill, [string[]]$Agents)
|
|
265
297
|
foreach ($agent in $Agents) {
|
|
266
|
-
$
|
|
267
|
-
if (-not (Test-Path $
|
|
268
|
-
throw "$Skill SKILL.md missing for targeted agent '$agent' ($
|
|
298
|
+
$checked = @($SkillAgentCandidateRoots[$agent] | ForEach-Object { Join-Path (Join-Path $_ $Skill) "SKILL.md" })
|
|
299
|
+
if (-not ($checked | Where-Object { Test-Path $_ })) {
|
|
300
|
+
throw "$Skill SKILL.md missing for targeted agent '$agent' (checked: $($checked -join ', '))"
|
|
269
301
|
}
|
|
270
302
|
}
|
|
271
303
|
Write-Log INFO $Phase "skill_verified" "$Skill verified for every targeted agent" 0 "agents=$($Agents -join ',')"
|
|
@@ -275,7 +307,7 @@ function Assert-SkillInstalledForAgents {
|
|
|
275
307
|
# Module registry
|
|
276
308
|
# ==============================================================================
|
|
277
309
|
|
|
278
|
-
$ModuleOrder = @('base','node','bun','pi','go','dotenv','ee','skills','pi-workflows','herdr','gentle-ai','codex','antigravity','opencode','cockpit')
|
|
310
|
+
$ModuleOrder = @('base','node','bun','pi','go','dotenv','ee','skills','pi-workflows','herdr','gentle-ai','codex','antigravity','opencode','cockpit','rotator')
|
|
279
311
|
|
|
280
312
|
$ModuleDesc = [ordered]@{
|
|
281
313
|
'base' = 'System packages (build tools, git, gh, python, neovim, jq, imagemagick, go)'
|
|
@@ -293,8 +325,9 @@ $ModuleDesc = [ordered]@{
|
|
|
293
325
|
'antigravity' = 'Google Antigravity CLI (agy)'
|
|
294
326
|
'opencode' = 'opencode agent CLI (opencode-ai)'
|
|
295
327
|
'cockpit' = 'cockpit-tools desktop GUI app (optional, CC BY-NC-SA)'
|
|
328
|
+
'rotator' = 'tuxevil-rotator multi-account Gemini/Antigravity gateway (optional, opt-in)'
|
|
296
329
|
}
|
|
297
|
-
$ModuleOptional = @{ 'cockpit' = $true }
|
|
330
|
+
$ModuleOptional = @{ 'cockpit' = $true; 'rotator' = $true }
|
|
298
331
|
|
|
299
332
|
# ==============================================================================
|
|
300
333
|
# Modules
|
|
@@ -649,6 +682,78 @@ function Mod-Cockpit {
|
|
|
649
682
|
}
|
|
650
683
|
}
|
|
651
684
|
|
|
685
|
+
function Mod-Rotator {
|
|
686
|
+
Write-Log INFO "rotator" "start" "tuxevil-rotator gateway"
|
|
687
|
+
# Detect a desktop cockpit-tools data dir by marker file only; never read tokens.
|
|
688
|
+
$candidates = @(
|
|
689
|
+
(Join-Path $HOME ".antigravity_cockpit")
|
|
690
|
+
(Join-Path $HOME ".local\share\cockpit-tools")
|
|
691
|
+
(Join-Path $HOME ".config\cockpit-tools")
|
|
692
|
+
(Join-Path $HOME ".wizard-ai\cockpit-tools")
|
|
693
|
+
(Join-Path $HOME "Library\Application Support\cockpit-tools")
|
|
694
|
+
)
|
|
695
|
+
if ($env:APPDATA) { $candidates += Join-Path $env:APPDATA "cockpit-tools" }
|
|
696
|
+
if ($env:LOCALAPPDATA) { $candidates += Join-Path $env:LOCALAPPDATA "cockpit-tools" }
|
|
697
|
+
|
|
698
|
+
$cockpitDir = ""
|
|
699
|
+
foreach ($dir in $candidates) {
|
|
700
|
+
if ((Test-Path -LiteralPath (Join-Path $dir "accounts.json") -PathType Leaf) -or
|
|
701
|
+
(Test-Path -LiteralPath (Join-Path $dir "account-token.key") -PathType Leaf)) {
|
|
702
|
+
$cockpitDir = $dir
|
|
703
|
+
break
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if ($cockpitDir) {
|
|
707
|
+
Write-Log INFO "rotator" "cockpit_detected" "cockpit-tools data directory detected" 0 "dir=$cockpitDir"
|
|
708
|
+
} else {
|
|
709
|
+
Write-Log INFO "rotator" "cockpit_absent" "No cockpit-tools data directory detected; the rotator can still use its own accounts" 0
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
# Non-fatal health probe.
|
|
713
|
+
$gw = "http://localhost:51200/v1/models"
|
|
714
|
+
try {
|
|
715
|
+
$response = Invoke-WebRequest -Uri $gw -Headers @{ Authorization = "Bearer tuxevil" } -TimeoutSec 5
|
|
716
|
+
$body = $response.Content
|
|
717
|
+
$count = if ($body) { ([regex]::Matches($body, '"id"')).Count } else { 0 }
|
|
718
|
+
Write-Log INFO "rotator" "gateway_up" "tuxevil-rotator gateway is reachable" 0 "url=$gw;models=$count"
|
|
719
|
+
} catch {
|
|
720
|
+
Write-Log INFO "rotator" "gateway_down" "tuxevil-rotator gateway not reachable; start it with 'tuxevil-rotator start'" 0 "url=$gw"
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
# Install the CLI idempotently. Never runs login/start or writes secrets.
|
|
724
|
+
if (Test-Cmd tuxevil-rotator) {
|
|
725
|
+
Write-Log INFO "rotator" "already_present" "tuxevil-rotator already installed"
|
|
726
|
+
} else {
|
|
727
|
+
if (-not (Test-Cmd npm)) { throw "npm not found; tuxevil-rotator cannot be installed" }
|
|
728
|
+
Invoke-Step -Phase "rotator" -Action { npm install -g tuxevil-rotator }
|
|
729
|
+
if (-not (Test-Cmd tuxevil-rotator)) {
|
|
730
|
+
Write-Log ERROR "rotator" "install_missing" "tuxevil-rotator not found on PATH after npm install"
|
|
731
|
+
throw "tuxevil-rotator not found on PATH after npm install"
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (Test-Cmd pi) {
|
|
735
|
+
Invoke-Step -Phase "rotator" -Action { pi install "github:darkrei08/pi-cockpit-tools-sync" }
|
|
736
|
+
$piSettings = Join-Path $HOME ".pi\agent\settings.json"
|
|
737
|
+
if (-not (Test-Path -LiteralPath $piSettings -PathType Leaf) -or
|
|
738
|
+
-not (Select-String -LiteralPath $piSettings -SimpleMatch "github:darkrei08/pi-cockpit-tools-sync" -Quiet)) {
|
|
739
|
+
Write-Log ERROR "rotator" "pi_extension_missing" "Pi did not register cockpit sync extension" 1 "expected=$piSettings"
|
|
740
|
+
throw "Pi did not register cockpit sync extension"
|
|
741
|
+
}
|
|
742
|
+
Write-Log INFO "rotator" "pi_extension_verified" "Cockpit sync extension registered in Pi" 0 "path=$piSettings"
|
|
743
|
+
} else {
|
|
744
|
+
Write-Log INFO "rotator" "pi_extension_skipped" "pi not found; cockpit sync extension was not installed"
|
|
745
|
+
}
|
|
746
|
+
@"
|
|
747
|
+
tuxevil-rotator installed. To use the multi-account Gemini/Antigravity gateway:
|
|
748
|
+
tuxevil-rotator login # add a Google Antigravity account (repeat to add more)
|
|
749
|
+
tuxevil-rotator import # or bulk-import accounts from a cockpit-tools JSON
|
|
750
|
+
tuxevil-rotator start # start the rotating proxy on http://localhost:51200
|
|
751
|
+
Pi reaches it through the 'tuxevil-rotator' provider configured in your dotenv.
|
|
752
|
+
The cockpit sync extension provides /cockpit-sync, /cockpit-provision, and /cockpit-proxy.
|
|
753
|
+
Login/start are never run by setup-ai and no tokens are read or stored.
|
|
754
|
+
"@ | Tee-Object -FilePath $HumanLog -Append | Out-Host
|
|
755
|
+
}
|
|
756
|
+
|
|
652
757
|
$ModuleFn = @{
|
|
653
758
|
'base' = ${function:Mod-Base}; 'node' = ${function:Mod-Node}; 'bun' = ${function:Mod-Bun}
|
|
654
759
|
'pi' = ${function:Mod-Pi}; 'go' = ${function:Mod-Go}; 'dotenv' = ${function:Mod-Dotenv}; 'ee' = ${function:Mod-Ee}
|
|
@@ -656,7 +761,7 @@ $ModuleFn = @{
|
|
|
656
761
|
'pi-workflows' = ${function:Mod-PiWorkflows}; 'herdr' = ${function:Mod-Herdr}
|
|
657
762
|
'gentle-ai' = ${function:Mod-GentleAi}
|
|
658
763
|
'codex' = ${function:Mod-Codex}; 'antigravity' = ${function:Mod-Antigravity}
|
|
659
|
-
'opencode' = ${function:Mod-Opencode}; 'cockpit' = ${function:Mod-Cockpit}
|
|
764
|
+
'opencode' = ${function:Mod-Opencode}; 'cockpit' = ${function:Mod-Cockpit}; 'rotator' = ${function:Mod-Rotator}
|
|
660
765
|
}
|
|
661
766
|
|
|
662
767
|
# ==============================================================================
|
package/setup-ai.sh
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# ==============================================================================
|
|
4
4
|
# AI Dev Suite — Engineering Excellence Edition
|
|
5
|
-
# Version: 3.
|
|
5
|
+
# Version: 3.3.0
|
|
6
6
|
#
|
|
7
7
|
# Cross-platform (macOS + all major Linux distros) installer for an AI coding
|
|
8
8
|
# toolchain. Windows is handled by the sibling setup-ai.ps1; the Node launcher
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
set -Eeuo pipefail
|
|
27
27
|
IFS=$'\n\t'
|
|
28
28
|
|
|
29
|
-
SCRIPT_VERSION="3.
|
|
29
|
+
SCRIPT_VERSION="3.3.0"
|
|
30
30
|
|
|
31
31
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
32
|
LOG_DIR="${SCRIPT_DIR}/logs"
|
|
@@ -305,7 +305,7 @@ EOF
|
|
|
305
305
|
# via --all or an explicit --only.
|
|
306
306
|
# ==============================================================================
|
|
307
307
|
|
|
308
|
-
MODULE_ORDER=(base node bun pi go dotenv ee skills pi-workflows herdr gentle-ai codex antigravity opencode cockpit)
|
|
308
|
+
MODULE_ORDER=(base node bun pi go dotenv ee skills pi-workflows herdr gentle-ai codex antigravity opencode cockpit rotator)
|
|
309
309
|
|
|
310
310
|
module_desc() {
|
|
311
311
|
case "$1" in
|
|
@@ -324,12 +324,13 @@ module_desc() {
|
|
|
324
324
|
antigravity) printf '%s\n' "Google Antigravity CLI (agy)" ;;
|
|
325
325
|
opencode) printf '%s\n' "opencode agent CLI (opencode-ai)" ;;
|
|
326
326
|
cockpit) printf '%s\n' "cockpit-tools desktop GUI app (optional, CC BY-NC-SA)" ;;
|
|
327
|
+
rotator) printf '%s\n' "tuxevil-rotator multi-account Gemini/Antigravity gateway (optional, opt-in)" ;;
|
|
327
328
|
*) return 1 ;;
|
|
328
329
|
esac
|
|
329
330
|
}
|
|
330
331
|
|
|
331
332
|
module_is_optional() {
|
|
332
|
-
[[ "$1" == "cockpit" ]]
|
|
333
|
+
[[ "$1" == "cockpit" || "$1" == "rotator" ]]
|
|
333
334
|
}
|
|
334
335
|
|
|
335
336
|
# ------------------------------------------------------------------------------
|
|
@@ -673,15 +674,34 @@ agent_skill_root() {
|
|
|
673
674
|
esac
|
|
674
675
|
}
|
|
675
676
|
|
|
677
|
+
# Candidate skill roots per agent (one per line): verification passes if
|
|
678
|
+
# SKILL.md exists under any of them. Every agent keeps its single existing root;
|
|
679
|
+
# Codex also accepts ${HOME}/.agents/skills because upstream `skills add
|
|
680
|
+
# --global` writes Codex skills there instead of ${HOME}/.codex/skills.
|
|
681
|
+
agent_skill_roots() {
|
|
682
|
+
agent_skill_root "$1" || return 1
|
|
683
|
+
if [[ "$1" == codex ]]; then
|
|
684
|
+
printf '%s\n' "${HOME}/.agents/skills"
|
|
685
|
+
fi
|
|
686
|
+
}
|
|
687
|
+
|
|
676
688
|
verify_skill_for_agents() {
|
|
677
689
|
local phase="$1" skill="$2"; shift 2
|
|
678
|
-
local agent root
|
|
690
|
+
local agent root found checked
|
|
679
691
|
for agent in "$@"; do
|
|
680
|
-
|
|
681
|
-
|
|
692
|
+
found=0
|
|
693
|
+
checked=""
|
|
694
|
+
while IFS= read -r root; do
|
|
695
|
+
checked="${checked:+${checked}, }${root}/${skill}/SKILL.md"
|
|
696
|
+
if [[ -f "${root}/${skill}/SKILL.md" ]]; then
|
|
697
|
+
found=1
|
|
698
|
+
break
|
|
699
|
+
fi
|
|
700
|
+
done < <(agent_skill_roots "${agent}")
|
|
701
|
+
if (( found == 0 )); then
|
|
682
702
|
log_event "ERROR" "${phase}" "skill_missing" \
|
|
683
703
|
"Skill SKILL.md missing for targeted agent" 1 \
|
|
684
|
-
"agent=${agent};skill=${skill};
|
|
704
|
+
"agent=${agent};skill=${skill};checked=${checked}"
|
|
685
705
|
return 1
|
|
686
706
|
fi
|
|
687
707
|
done
|
|
@@ -1109,6 +1129,70 @@ mod_cockpit() {
|
|
|
1109
1129
|
esac
|
|
1110
1130
|
}
|
|
1111
1131
|
|
|
1132
|
+
# --- rotator (opt-in: tuxevil-rotator multi-account gateway) -----------------
|
|
1133
|
+
mod_rotator() {
|
|
1134
|
+
section "tuxevil-rotator gateway"
|
|
1135
|
+
# Detect a desktop cockpit-tools data dir by marker file only; never read tokens.
|
|
1136
|
+
local -a candidates=(
|
|
1137
|
+
"${HOME}/.antigravity_cockpit"
|
|
1138
|
+
"${HOME}/.local/share/cockpit-tools"
|
|
1139
|
+
"${HOME}/.config/cockpit-tools"
|
|
1140
|
+
"${HOME}/.wizard-ai/cockpit-tools"
|
|
1141
|
+
"${HOME}/Library/Application Support/cockpit-tools"
|
|
1142
|
+
)
|
|
1143
|
+
# Windows-only locations, appended only when set (mirrors the ps1 behavior).
|
|
1144
|
+
[[ -n "${APPDATA:-}" ]] && candidates+=("${APPDATA}/cockpit-tools")
|
|
1145
|
+
[[ -n "${LOCALAPPDATA:-}" ]] && candidates+=("${LOCALAPPDATA}/cockpit-tools")
|
|
1146
|
+
local dir cockpit_dir=""
|
|
1147
|
+
for dir in "${candidates[@]}"; do
|
|
1148
|
+
if [[ -f "${dir}/accounts.json" || -f "${dir}/account-token.key" ]]; then
|
|
1149
|
+
cockpit_dir="${dir}"; break
|
|
1150
|
+
fi
|
|
1151
|
+
done
|
|
1152
|
+
if [[ -n "${cockpit_dir}" ]]; then
|
|
1153
|
+
log_event "INFO" "rotator" "cockpit_detected" "cockpit-tools data directory detected" 0 "dir=${cockpit_dir}"
|
|
1154
|
+
else
|
|
1155
|
+
log_event "INFO" "rotator" "cockpit_absent" "No cockpit-tools data directory detected; the rotator can still use its own accounts" 0
|
|
1156
|
+
fi
|
|
1157
|
+
# Non-fatal health probe.
|
|
1158
|
+
local gw="http://localhost:51200/v1/models" body count
|
|
1159
|
+
if body="$(curl -fsS -m 5 -H 'Authorization: Bearer tuxevil' "${gw}" 2>/dev/null)"; then
|
|
1160
|
+
# curl -fsS already proved reachability; the count is informational only
|
|
1161
|
+
# (awk always exits 0, so no operational failure is masked here).
|
|
1162
|
+
count="$(printf '%s' "${body}" | awk '{c+=gsub(/"id"/,"&")} END{print c+0}')"
|
|
1163
|
+
log_event "INFO" "rotator" "gateway_up" "tuxevil-rotator gateway is reachable" 0 "url=${gw};models=${count}"
|
|
1164
|
+
else
|
|
1165
|
+
log_event "INFO" "rotator" "gateway_down" "tuxevil-rotator gateway not reachable; start it with 'tuxevil-rotator start'" 0 "url=${gw}"
|
|
1166
|
+
fi
|
|
1167
|
+
# Install the CLI idempotently. Never runs login/start or writes secrets.
|
|
1168
|
+
if command -v tuxevil-rotator >/dev/null 2>&1; then
|
|
1169
|
+
log_event "INFO" "rotator" "already_present" "tuxevil-rotator already installed" 0
|
|
1170
|
+
else
|
|
1171
|
+
run_cmd "rotator" npm install --global tuxevil-rotator
|
|
1172
|
+
require_command tuxevil-rotator
|
|
1173
|
+
fi
|
|
1174
|
+
if command -v pi >/dev/null 2>&1; then
|
|
1175
|
+
run_cmd "rotator" pi install "github:darkrei08/pi-cockpit-tools-sync"
|
|
1176
|
+
local pi_settings="${PI_AGENT_DIR}/settings.json"
|
|
1177
|
+
if [[ ! -f "${pi_settings}" ]] || ! grep -Fq 'github:darkrei08/pi-cockpit-tools-sync' "${pi_settings}"; then
|
|
1178
|
+
log_event "ERROR" "rotator" "pi_extension_missing" "Pi did not register cockpit sync extension" 1 "expected=${pi_settings}"
|
|
1179
|
+
return 1
|
|
1180
|
+
fi
|
|
1181
|
+
log_event "INFO" "rotator" "pi_extension_verified" "Cockpit sync extension registered in Pi" 0 "path=${pi_settings}"
|
|
1182
|
+
else
|
|
1183
|
+
log_event "INFO" "rotator" "pi_extension_skipped" "pi not found; cockpit sync extension was not installed" 0
|
|
1184
|
+
fi
|
|
1185
|
+
cat <<'HINT' | tee -a "${HUMAN_LOG}"
|
|
1186
|
+
tuxevil-rotator installed. To use the multi-account Gemini/Antigravity gateway:
|
|
1187
|
+
tuxevil-rotator login # add a Google Antigravity account (repeat to add more)
|
|
1188
|
+
tuxevil-rotator import # or bulk-import accounts from a cockpit-tools JSON
|
|
1189
|
+
tuxevil-rotator start # start the rotating proxy on http://localhost:51200
|
|
1190
|
+
Pi reaches it through the 'tuxevil-rotator' provider configured in your dotenv.
|
|
1191
|
+
The cockpit sync extension provides /cockpit-sync, /cockpit-provision, and /cockpit-proxy.
|
|
1192
|
+
Login/start are never run by setup-ai and no tokens are read or stored.
|
|
1193
|
+
HINT
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1112
1196
|
# ==============================================================================
|
|
1113
1197
|
# Shell environment
|
|
1114
1198
|
# ==============================================================================
|