feat: rename rails/ to tools/ and add service tool suites

Rename the `rails/` directory to `tools/` for agent discoverability —
agents frequently failed to locate helper scripts due to the non-intuitive
directory name. Add backward-compat symlink `rails/ → tools/`.

New tool suites:
- Authentik: auth-token, user-list, user-create, group-list, app-list,
  flow-list, admin-status (8 scripts)
- Coolify: team-list, project-list, service-list, service-status, deploy,
  env-set (7 scripts)
- Woodpecker: pipeline-list, pipeline-status, pipeline-trigger (3 stubs)
- GLPI: session-init, computer-list, ticket-list, ticket-create, user-list
  (6 scripts)
- Health: stack-health.sh — stack-wide connectivity check

Infrastructure:
- Shared credential loader at tools/_lib/credentials.sh
- install.sh creates symlink + chmod on tool scripts
- All ~253 rails/ path references updated across 68+ files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 11:51:39 -06:00
parent 248db8935c
commit 80c3680ccb
158 changed files with 2481 additions and 213 deletions

65
tools/coolify/README.md Normal file
View File

@@ -0,0 +1,65 @@
# Coolify Tool Suite
Manage Coolify container deployment platform (projects, services, deployments, environment variables).
## Prerequisites
- `jq` and `curl` installed
- Coolify credentials in `~/src/jarvis-brain/credentials.json` (or `$MOSAIC_CREDENTIALS_FILE`)
- Required fields: `coolify.url`, `coolify.app_token`
## Scripts
| Script | Purpose |
|--------|---------|
| `team-list.sh` | List teams |
| `project-list.sh` | List projects |
| `service-list.sh` | List all services |
| `service-status.sh` | Get service details and status |
| `deploy.sh` | Trigger service deployment |
| `env-set.sh` | Set environment variable on a service |
## Common Options
- `-f json` — JSON output (default: table)
- `-u uuid` — Service UUID (for service-specific operations)
- `-h` — Show help
## API Reference
- Base URL: `http://10.1.1.44:8000`
- API prefix: `/api/v1/`
- Auth: Bearer token in `Authorization` header
- Rate limit: 200 requests per interval
## Known Limitations
- **FQDN updates on compose sub-apps not supported via API.** Workaround: update directly in Coolify's PostgreSQL DB (`coolify-db` container, `service_applications` table).
- **Compose must be base64-encoded** in `docker_compose_raw` field when creating services via API.
- **Don't send `type` with `docker_compose_raw`** — API rejects payloads with both fields.
## Coolify Magic Variables
Coolify reads special env vars from compose files:
- `SERVICE_FQDN_{NAME}_{PORT}` — assigns a domain to a compose service
- `SERVICE_URL_{NAME}_{PORT}` — internal URL reference
- Must use list-style env syntax (`- SERVICE_FQDN_API_3001`), NOT dict-style.
## Examples
```bash
# List all projects
~/.config/mosaic/tools/coolify/project-list.sh
# List services as JSON
~/.config/mosaic/tools/coolify/service-list.sh -f json
# Check service status
~/.config/mosaic/tools/coolify/service-status.sh -u <uuid>
# Set an env var
~/.config/mosaic/tools/coolify/env-set.sh -u <uuid> -k DATABASE_URL -v "postgres://..."
# Deploy a service
~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>
```

61
tools/coolify/deploy.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
#
# deploy.sh — Trigger Coolify service deployment
#
# Usage: deploy.sh -u <uuid> [-f]
#
# Options:
# -u uuid Service UUID (required)
# -f Force restart (stop then start)
# -h Show this help
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
UUID=""
FORCE=false
while getopts "u:fh" opt; do
case $opt in
u) UUID="$OPTARG" ;;
f) FORCE=true ;;
h) head -11 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 -u <uuid> [-f]" >&2; exit 1 ;;
esac
done
if [[ -z "$UUID" ]]; then
echo "Error: -u uuid is required" >&2
exit 1
fi
if [[ "$FORCE" == "true" ]]; then
echo "Stopping service $UUID..."
curl -s -o /dev/null -w "" \
-X POST \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/services/${UUID}/stop"
sleep 2
fi
echo "Starting service $UUID..."
response=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/services/${UUID}/start")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" && "$http_code" != "201" && "$http_code" != "202" ]]; then
echo "Error: Deployment failed (HTTP $http_code)" >&2
echo "$body" | jq -r '.' 2>/dev/null >&2 || echo "$body" >&2
exit 1
fi
echo "Deployment triggered successfully for service $UUID"
echo "$body" | jq -r '.message // empty' 2>/dev/null || true

65
tools/coolify/env-set.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
#
# env-set.sh — Set environment variable on a Coolify service
#
# Usage: env-set.sh -u <uuid> -k <key> -v <value> [--preview]
#
# Options:
# -u uuid Service UUID (required)
# -k key Environment variable name (required)
# -v value Environment variable value (required)
# --preview Set as preview-only variable
# -h Show this help
#
# Note: Changes take effect on next deploy/restart.
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
UUID=""
KEY=""
VALUE=""
IS_PREVIEW="false"
while [[ $# -gt 0 ]]; do
case $1 in
-u) UUID="$2"; shift 2 ;;
-k) KEY="$2"; shift 2 ;;
-v) VALUE="$2"; shift 2 ;;
--preview) IS_PREVIEW="true"; shift ;;
-h) head -15 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 -u <uuid> -k <key> -v <value> [--preview]" >&2; exit 1 ;;
esac
done
if [[ -z "$UUID" || -z "$KEY" || -z "$VALUE" ]]; then
echo "Error: -u uuid, -k key, and -v value are required" >&2
exit 1
fi
payload=$(jq -n \
--arg key "$KEY" \
--arg value "$VALUE" \
--argjson preview "$IS_PREVIEW" \
'{key: $key, value: $value, is_preview: $preview}')
response=$(curl -s -w "\n%{http_code}" \
-X PATCH \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload" \
"${COOLIFY_URL}/api/v1/services/${UUID}/envs")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" && "$http_code" != "201" ]]; then
echo "Error: Failed to set environment variable (HTTP $http_code)" >&2
echo "$body" | jq -r '.' 2>/dev/null >&2 || echo "$body" >&2
exit 1
fi
echo "Set $KEY on service $UUID"
echo "Note: Redeploy the service to apply the change"

52
tools/coolify/project-list.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
#
# project-list.sh — List Coolify projects
#
# Usage: project-list.sh [-f format]
#
# Options:
# -f format Output format: table (default), json
# -h Show this help
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
FORMAT="table"
while getopts "f:h" opt; do
case $opt in
f) FORMAT="$OPTARG" ;;
h) head -10 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 [-f format]" >&2; exit 1 ;;
esac
done
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/projects")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
echo "Error: Failed to list projects (HTTP $http_code)" >&2
exit 1
fi
if [[ "$FORMAT" == "json" ]]; then
echo "$body" | jq '.'
exit 0
fi
echo "UUID NAME DESCRIPTION"
echo "------------------------------------ ---------------------------- ----------------------------------------"
echo "$body" | jq -r '.[] | [
.uuid,
.name,
(.description // "—")
] | @tsv' | while IFS=$'\t' read -r uuid name desc; do
printf "%-36s %-28s %s\n" "$uuid" "${name:0:28}" "${desc:0:40}"
done

53
tools/coolify/service-list.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#
# service-list.sh — List Coolify services
#
# Usage: service-list.sh [-f format]
#
# Options:
# -f format Output format: table (default), json
# -h Show this help
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
FORMAT="table"
while getopts "f:h" opt; do
case $opt in
f) FORMAT="$OPTARG" ;;
h) head -10 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 [-f format]" >&2; exit 1 ;;
esac
done
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/services")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
echo "Error: Failed to list services (HTTP $http_code)" >&2
exit 1
fi
if [[ "$FORMAT" == "json" ]]; then
echo "$body" | jq '.'
exit 0
fi
echo "UUID NAME TYPE STATUS"
echo "------------------------------------ ---------------------------- ------------ ----------"
echo "$body" | jq -r '.[] | [
.uuid,
.name,
(.type // "unknown"),
(.status // "unknown")
] | @tsv' | while IFS=$'\t' read -r uuid name type status; do
printf "%-36s %-28s %-12s %s\n" "$uuid" "${name:0:28}" "${type:0:12}" "$status"
done

62
tools/coolify/service-status.sh Executable file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/env bash
#
# service-status.sh — Get Coolify service status and details
#
# Usage: service-status.sh -u <uuid> [-f format]
#
# Options:
# -u uuid Service UUID (required)
# -f format Output format: table (default), json
# -h Show this help
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
UUID=""
FORMAT="table"
while getopts "u:f:h" opt; do
case $opt in
u) UUID="$OPTARG" ;;
f) FORMAT="$OPTARG" ;;
h) head -12 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 -u <uuid> [-f format]" >&2; exit 1 ;;
esac
done
if [[ -z "$UUID" ]]; then
echo "Error: -u uuid is required" >&2
exit 1
fi
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/services/${UUID}")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
echo "Error: Failed to get service status (HTTP $http_code)" >&2
exit 1
fi
if [[ "$FORMAT" == "json" ]]; then
echo "$body" | jq '.'
exit 0
fi
echo "Service Details"
echo "==============="
echo "$body" | jq -r '
" UUID: \(.uuid)\n" +
" Name: \(.name)\n" +
" Type: \(.type // "unknown")\n" +
" Status: \(.status // "unknown")\n" +
" FQDN: \(.fqdn // "none")\n" +
" Created: \(.created_at // "unknown")\n" +
" Updated: \(.updated_at // "unknown")"
'

52
tools/coolify/team-list.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
#
# team-list.sh — List Coolify teams
#
# Usage: team-list.sh [-f format]
#
# Options:
# -f format Output format: table (default), json
# -h Show this help
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
load_credentials coolify
FORMAT="table"
while getopts "f:h" opt; do
case $opt in
f) FORMAT="$OPTARG" ;;
h) head -10 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
*) echo "Usage: $0 [-f format]" >&2; exit 1 ;;
esac
done
response=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $COOLIFY_TOKEN" \
-H "Content-Type: application/json" \
"${COOLIFY_URL}/api/v1/teams")
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
echo "Error: Failed to list teams (HTTP $http_code)" >&2
exit 1
fi
if [[ "$FORMAT" == "json" ]]; then
echo "$body" | jq '.'
exit 0
fi
echo "ID NAME DESCRIPTION"
echo "---- ---------------------------- ----------------------------------------"
echo "$body" | jq -r '.[] | [
(.id | tostring),
.name,
(.description // "—")
] | @tsv' | while IFS=$'\t' read -r id name desc; do
printf "%-4s %-28s %s\n" "$id" "${name:0:28}" "${desc:0:40}"
done