feat: rename rails/ to tools/ and add service tool suites (#4)
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #4.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
node_modules/
|
||||
rails
|
||||
|
||||
@@ -30,8 +30,8 @@ If any required file is missing, you MUST stop and report the missing file.
|
||||
3. Routine repository operations are NOT escalation triggers. Use escalation triggers only from this contract.
|
||||
4. For source-code delivery, completion is forbidden at PR-open stage.
|
||||
5. Completion requires merged PR to `main` + terminal green CI + linked issue/internal task closed.
|
||||
6. Before push or merge, you MUST run queue guard: `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push|merge`.
|
||||
7. For issue/PR/milestone operations, you MUST use Mosaic wrappers first (`~/.config/mosaic/rails/git/*.sh`).
|
||||
6. Before push or merge, you MUST run queue guard: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge`.
|
||||
7. For issue/PR/milestone operations, you MUST use Mosaic wrappers first (`~/.config/mosaic/tools/git/*.sh`).
|
||||
8. If any required wrapper command fails, status is `blocked`; report the exact failed wrapper command and stop.
|
||||
9. Do NOT stop at "PR created". Do NOT ask "should I merge?" Do NOT ask "should I close the issue?".
|
||||
|
||||
@@ -63,7 +63,7 @@ If any required file is missing, you MUST stop and report the missing file.
|
||||
24. Deployment ownership is REQUIRED when deployment is in scope and target access is configured.
|
||||
25. For container deployments, you MUST use immutable image tags (`sha-*`, `vX.Y.Z-rc.N`) with digest-first promotion; `latest` is forbidden as a deployment reference.
|
||||
26. If an external git provider is available (Gitea/GitHub/GitLab), you MUST create or update issue(s) and link them in `docs/TASKS.md` before coding; if unavailable, use `TASKS:<id>` internal refs in `docs/TASKS.md`.
|
||||
27. For provider operations (issue/PR/milestone), you MUST detect platform first and use `~/.config/mosaic/rails/git/*.sh` wrappers before any raw provider CLI/API calls.
|
||||
27. For provider operations (issue/PR/milestone), you MUST detect platform first and use `~/.config/mosaic/tools/git/*.sh` wrappers before any raw provider CLI/API calls.
|
||||
28. Direct `gh`/`tea`/`glab` commands are forbidden as first choice when a Mosaic wrapper exists; use raw commands only as documented fallback.
|
||||
29. If the mission is orchestration-oriented (contains "orchestrate", issue/milestone coordination, or multi-task execution), you MUST load and follow `~/.config/mosaic/guides/ORCHESTRATOR.md` before taking action.
|
||||
30. At session start, you MUST declare the operating mode in your first response before any tool calls or implementation steps.
|
||||
@@ -72,7 +72,7 @@ If any required file is missing, you MUST stop and report the missing file.
|
||||
33. For explicit review-only missions, the first line MUST be exactly: `Now initiating Review mode...`
|
||||
34. For source-code delivery through PR workflow, completion is forbidden until the PR is merged to `main`, CI/pipeline status is terminal green, and linked issue/internal task is closed.
|
||||
35. If merge/CI/issue-closure operations fail, you MUST report a blocker with the exact failed wrapper command and stop instead of declaring completion.
|
||||
36. Before push or PR merge, you MUST run CI queue guard and wait if the project has running/queued pipelines: `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push|merge`.
|
||||
36. Before push or PR merge, you MUST run CI queue guard and wait if the project has running/queued pipelines: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge`.
|
||||
|
||||
## Mode Declaration Protocol (Hard Rule)
|
||||
|
||||
|
||||
@@ -24,19 +24,19 @@ Scope:
|
||||
|
||||
### MF-001 (QA rails path correction)
|
||||
Updated:
|
||||
- `rails/qa/qa-hook-wrapper.sh`
|
||||
- `rails/qa/qa-hook-stdin.sh`
|
||||
- `rails/qa/qa-hook-handler.sh`
|
||||
- `rails/qa/remediation-hook-handler.sh`
|
||||
- `rails/qa/qa-queue-monitor.sh`
|
||||
- `tools/qa/qa-hook-wrapper.sh`
|
||||
- `tools/qa/qa-hook-stdin.sh`
|
||||
- `tools/qa/qa-hook-handler.sh`
|
||||
- `tools/qa/remediation-hook-handler.sh`
|
||||
- `tools/qa/qa-queue-monitor.sh`
|
||||
|
||||
Change:
|
||||
- Standardized handler paths to `~/.config/mosaic/rails/qa/...`.
|
||||
- Standardized handler paths to `~/.config/mosaic/tools/qa/...`.
|
||||
|
||||
### MF-002 + MF-003 (conditional loading/context detection)
|
||||
Updated:
|
||||
- `rails/bootstrap/agent-lint.sh`
|
||||
- `rails/bootstrap/agent-upgrade.sh`
|
||||
- `tools/bootstrap/agent-lint.sh`
|
||||
- `tools/bootstrap/agent-upgrade.sh`
|
||||
- `templates/agent/SPEC.md`
|
||||
|
||||
Change:
|
||||
@@ -58,7 +58,7 @@ Updated:
|
||||
- `skills/pr-reviewer/SKILL.md`
|
||||
|
||||
Change:
|
||||
- Replaced all `~/.claude/scripts/git/...` with `~/.config/mosaic/rails/git/...`.
|
||||
- Replaced all `~/.claude/scripts/git/...` with `~/.config/mosaic/tools/git/...`.
|
||||
- Replaced `~/.claude/skills/...` with `~/.config/mosaic/skills/...`.
|
||||
|
||||
### MF-006 (worktree skill docs hierarchy)
|
||||
@@ -109,7 +109,7 @@ These are required to support existing Claude runtime integration while keeping
|
||||
Executed checks:
|
||||
- `rg -n "~/.claude|\\.claude/|agent-guides" ~/src/agent-skills -S`
|
||||
- Result: no matches after remediation.
|
||||
- `rg -n "~/.config/mosaic/rails/(qa-hook|remediation-hook|qa-queue-monitor)" ~/src/mosaic-bootstrap -S`
|
||||
- `rg -n "~/.config/mosaic/tools/(qa-hook|remediation-hook|qa-queue-monitor)" ~/src/mosaic-bootstrap -S`
|
||||
- Result: no invalid old-style QA rail paths remain.
|
||||
- Installed runtime validation:
|
||||
- `~/.config/mosaic` contains `rails/git`, `rails/portainer`, `rails/cicd`, `skills`, and `bin` tooling.
|
||||
- `~/.config/mosaic` contains `tools/git`, `tools/portainer`, `tools/cicd`, `skills`, and `bin` tooling.
|
||||
|
||||
@@ -103,7 +103,7 @@ You can still launch runtimes directly (`claude`, `codex`, etc.) — thin runtim
|
||||
├── bin/ ← CLI tools (mosaic, mosaic-init, mosaic-doctor, etc.)
|
||||
├── dist/ ← Bundled wizard (mosaic-wizard.mjs)
|
||||
├── guides/ ← Operational guides
|
||||
├── rails/ ← Quality rails, git scripts, portainer scripts
|
||||
├── tools/ ← Tool suites: git, portainer, authentik, coolify, codex, etc.
|
||||
├── runtime/ ← Runtime adapters + runtime-specific references
|
||||
│ ├── claude/CLAUDE.md
|
||||
│ ├── claude/RUNTIME.md
|
||||
|
||||
12
STANDARDS.md
12
STANDARDS.md
@@ -12,16 +12,16 @@ Master/slave model:
|
||||
2. Load project-local `AGENTS.md` next.
|
||||
3. Respect repository-specific tooling and workflows.
|
||||
4. Use lifecycle scripts when available (`scripts/agent/*.sh`).
|
||||
5. Use shared rails/guides from `~/.config/mosaic` as canonical references.
|
||||
5. Use shared tools/guides from `~/.config/mosaic` as canonical references.
|
||||
|
||||
## Non-Negotiables
|
||||
|
||||
- Data files are authoritative; generated views are derived artifacts.
|
||||
- Pull before edits when collaborating in shared repos.
|
||||
- Run validation checks before claiming completion.
|
||||
- Apply quality rails from `~/.config/mosaic/rails/` when relevant (review, QA, git workflow).
|
||||
- For project-level mechanical enforcement templates, use `~/.config/mosaic/rails/quality/` via `~/.config/mosaic/bin/mosaic-quality-apply`.
|
||||
- For runtime-agnostic delegation/orchestration, use `~/.config/mosaic/rails/orchestrator-matrix/` with repo-local `.mosaic/orchestrator/` state.
|
||||
- Apply quality tools from `~/.config/mosaic/tools/` when relevant (review, QA, git workflow).
|
||||
- For project-level mechanical enforcement templates, use `~/.config/mosaic/tools/quality/` via `~/.config/mosaic/bin/mosaic-quality-apply`.
|
||||
- For runtime-agnostic delegation/orchestration, use `~/.config/mosaic/tools/orchestrator-matrix/` with repo-local `.mosaic/orchestrator/` state.
|
||||
- Avoid hardcoded secrets and token leakage in remotes/commits.
|
||||
- Do not perform destructive git/file actions without explicit instruction.
|
||||
- Browser automation (Playwright, Cypress, Puppeteer) MUST run in headless mode. Never launch a visible browser — it collides with the user's display and active session.
|
||||
@@ -50,10 +50,10 @@ All runtime adapters should inject:
|
||||
|
||||
before task execution.
|
||||
|
||||
Runtime-compatible guides and rails are hosted at:
|
||||
Runtime-compatible guides and tools are hosted at:
|
||||
|
||||
- `~/.config/mosaic/guides/`
|
||||
- `~/.config/mosaic/rails/`
|
||||
- `~/.config/mosaic/tools/`
|
||||
- `~/.config/mosaic/profiles/` (runtime-neutral domain/workflow/stack presets)
|
||||
- `~/.config/mosaic/runtime/` (runtime-specific overlays)
|
||||
- `~/.config/mosaic/skills-local/` (local private skills shared across runtimes)
|
||||
|
||||
103
TOOLS.md
103
TOOLS.md
@@ -3,34 +3,104 @@
|
||||
Centralized reference for tools, credentials, and CLI patterns available across all projects.
|
||||
Project-specific tooling belongs in the project's `AGENTS.md`, not here.
|
||||
|
||||
## Mosaic Git Wrappers (Use First)
|
||||
All tool suites are located at `~/.config/mosaic/tools/`.
|
||||
|
||||
Mosaic wrappers at `~/.config/mosaic/rails/git/*.sh` handle platform detection and edge cases. Always use these before raw CLI commands.
|
||||
## Tool Suites
|
||||
|
||||
### Git Wrappers (Use First)
|
||||
|
||||
Mosaic wrappers at `~/.config/mosaic/tools/git/*.sh` handle platform detection and edge cases. Always use these before raw CLI commands.
|
||||
|
||||
```bash
|
||||
# Issues
|
||||
~/.config/mosaic/rails/git/issue-create.sh
|
||||
~/.config/mosaic/rails/git/issue-close.sh
|
||||
~/.config/mosaic/tools/git/issue-create.sh
|
||||
~/.config/mosaic/tools/git/issue-close.sh
|
||||
|
||||
# PRs
|
||||
~/.config/mosaic/rails/git/pr-create.sh
|
||||
~/.config/mosaic/rails/git/pr-merge.sh
|
||||
~/.config/mosaic/tools/git/pr-create.sh
|
||||
~/.config/mosaic/tools/git/pr-merge.sh
|
||||
|
||||
# Milestones
|
||||
~/.config/mosaic/rails/git/milestone-create.sh
|
||||
~/.config/mosaic/tools/git/milestone-create.sh
|
||||
|
||||
# CI queue guard (required before push/merge)
|
||||
~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push|merge
|
||||
~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge
|
||||
```
|
||||
|
||||
## Code Review (Codex)
|
||||
### Code Review (Codex)
|
||||
|
||||
```bash
|
||||
# Code quality review
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted
|
||||
```
|
||||
|
||||
# Security review
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted
|
||||
### Infrastructure — Portainer
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/portainer/stack-status.sh -n <stack-name>
|
||||
~/.config/mosaic/tools/portainer/stack-redeploy.sh -n <stack-name>
|
||||
~/.config/mosaic/tools/portainer/stack-list.sh
|
||||
~/.config/mosaic/tools/portainer/endpoint-list.sh
|
||||
```
|
||||
|
||||
### Infrastructure — Coolify
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/coolify/project-list.sh
|
||||
~/.config/mosaic/tools/coolify/service-list.sh
|
||||
~/.config/mosaic/tools/coolify/service-status.sh -u <uuid>
|
||||
~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>
|
||||
~/.config/mosaic/tools/coolify/env-set.sh -u <uuid> -k KEY -v VALUE
|
||||
```
|
||||
|
||||
### Identity — Authentik
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/authentik/user-list.sh
|
||||
~/.config/mosaic/tools/authentik/user-create.sh -u <username> -n <name> -e <email>
|
||||
~/.config/mosaic/tools/authentik/group-list.sh
|
||||
~/.config/mosaic/tools/authentik/app-list.sh
|
||||
~/.config/mosaic/tools/authentik/flow-list.sh
|
||||
~/.config/mosaic/tools/authentik/admin-status.sh
|
||||
```
|
||||
|
||||
### CI/CD — Woodpecker
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/woodpecker/pipeline-list.sh
|
||||
~/.config/mosaic/tools/woodpecker/pipeline-status.sh
|
||||
~/.config/mosaic/tools/woodpecker/pipeline-trigger.sh -b <branch>
|
||||
```
|
||||
|
||||
### IT Service — GLPI
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/glpi/ticket-list.sh
|
||||
~/.config/mosaic/tools/glpi/ticket-create.sh -t <title> -c <content>
|
||||
~/.config/mosaic/tools/glpi/computer-list.sh
|
||||
~/.config/mosaic/tools/glpi/user-list.sh
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
# Check all configured services
|
||||
~/.config/mosaic/tools/health/stack-health.sh
|
||||
|
||||
# Check a specific service
|
||||
~/.config/mosaic/tools/health/stack-health.sh -s portainer
|
||||
|
||||
# JSON output for automation
|
||||
~/.config/mosaic/tools/health/stack-health.sh -f json
|
||||
```
|
||||
|
||||
### Shared Credential Loader
|
||||
|
||||
```bash
|
||||
# Source in any script to load service credentials
|
||||
source ~/.config/mosaic/tools/_lib/credentials.sh
|
||||
load_credentials <service-name>
|
||||
# Supported: portainer, coolify, authentik, glpi, github, gitea-mosaicstack, gitea-usc, woodpecker
|
||||
```
|
||||
|
||||
## Git Providers
|
||||
@@ -42,16 +112,13 @@ Mosaic wrappers at `~/.config/mosaic/rails/git/*.sh` handle platform detection a
|
||||
## Credentials
|
||||
|
||||
**Location:** (configure your credential file path)
|
||||
**Loader:** `source ~/.config/mosaic/tools/_lib/credentials.sh && load_credentials <service>`
|
||||
|
||||
**Never expose actual values. Never commit credential files.**
|
||||
|
||||
## CLI Gotchas
|
||||
|
||||
(Add platform-specific CLI gotchas as you discover them. Examples: TTY requirements, default list limits, API fallback patterns.)
|
||||
|
||||
## Custom Tools
|
||||
|
||||
(Add any machine-specific tools, scripts, or workflows here.)
|
||||
(Add platform-specific CLI gotchas as you discover them.)
|
||||
|
||||
## Safety Defaults
|
||||
|
||||
|
||||
@@ -14,4 +14,4 @@ Use wrapper commands from `~/.config/mosaic/bin/` for lifecycle rituals.
|
||||
## Migration Note
|
||||
|
||||
Project-local `.claude/commands/*.md` should call `scripts/agent/*.sh` so behavior stays runtime-neutral.
|
||||
Guides and rails should resolve to `~/.config/mosaic/guides` and `~/.config/mosaic/rails` (linked into `~/.claude` for compatibility).
|
||||
Guides and tools should resolve to `~/.config/mosaic/guides` and `~/.config/mosaic/tools` (linked into `~/.claude` for compatibility).
|
||||
|
||||
@@ -90,10 +90,10 @@ bash scripts/agent/critical.sh
|
||||
bash scripts/agent/session-end.sh
|
||||
```
|
||||
|
||||
## Shared Rails
|
||||
## Shared Tools
|
||||
|
||||
- Quality and orchestration guides: `~/.config/mosaic/guides/`
|
||||
- Shared automation rails: `~/.config/mosaic/rails/`
|
||||
- Shared automation tools: `~/.config/mosaic/tools/`
|
||||
|
||||
## Repo-Specific Notes
|
||||
|
||||
@@ -108,7 +108,7 @@ fi
|
||||
|
||||
echo "[mosaic] Repo bootstrap complete: $TARGET_DIR"
|
||||
echo "[mosaic] Next: edit $TARGET_DIR/.mosaic/repo-hooks.sh with project workflows"
|
||||
echo "[mosaic] Optional: apply quality rails via ~/.config/mosaic/bin/mosaic-quality-apply --template <template> --target $TARGET_DIR"
|
||||
echo "[mosaic] Optional: apply quality tools via ~/.config/mosaic/bin/mosaic-quality-apply --template <template> --target $TARGET_DIR"
|
||||
echo "[mosaic] Optional: run orchestrator rail via ~/.config/mosaic/bin/mosaic-orchestrator-drain"
|
||||
echo "[mosaic] Optional: run detached orchestrator via bash $TARGET_DIR/scripts/agent/orchestrator-daemon.sh start"
|
||||
|
||||
@@ -119,8 +119,8 @@ if [[ -n "$QUALITY_TEMPLATE" ]]; then
|
||||
sed -i "s/^enabled:.*/enabled: true/" "$TARGET_DIR/.mosaic/quality-rails.yml"
|
||||
sed -i "s/^template:.*/template: \"$QUALITY_TEMPLATE\"/" "$TARGET_DIR/.mosaic/quality-rails.yml"
|
||||
fi
|
||||
echo "[mosaic] Applied quality rails template: $QUALITY_TEMPLATE"
|
||||
echo "[mosaic] Applied quality tools template: $QUALITY_TEMPLATE"
|
||||
else
|
||||
echo "[mosaic] WARN: mosaic-quality-apply not found; skipping quality rails apply" >&2
|
||||
echo "[mosaic] WARN: mosaic-quality-apply not found; skipping quality tools apply" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -149,9 +149,9 @@ expect_file "$MOSAIC_HOME/STANDARDS.md"
|
||||
expect_file "$MOSAIC_HOME/USER.md"
|
||||
expect_file "$MOSAIC_HOME/TOOLS.md"
|
||||
expect_dir "$MOSAIC_HOME/guides"
|
||||
expect_dir "$MOSAIC_HOME/rails"
|
||||
expect_dir "$MOSAIC_HOME/rails/quality"
|
||||
expect_dir "$MOSAIC_HOME/rails/orchestrator-matrix"
|
||||
expect_dir "$MOSAIC_HOME/tools"
|
||||
expect_dir "$MOSAIC_HOME/tools/quality"
|
||||
expect_dir "$MOSAIC_HOME/tools/orchestrator-matrix"
|
||||
expect_dir "$MOSAIC_HOME/profiles"
|
||||
expect_dir "$MOSAIC_HOME/templates/agent"
|
||||
expect_dir "$MOSAIC_HOME/skills"
|
||||
@@ -168,10 +168,10 @@ expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-drain"
|
||||
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-publish"
|
||||
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-consume"
|
||||
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-cycle"
|
||||
expect_file "$MOSAIC_HOME/rails/git/ci-queue-wait.sh"
|
||||
expect_file "$MOSAIC_HOME/rails/git/pr-ci-wait.sh"
|
||||
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/transport/matrix_transport.py"
|
||||
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/controller/tasks_md_sync.py"
|
||||
expect_file "$MOSAIC_HOME/tools/git/ci-queue-wait.sh"
|
||||
expect_file "$MOSAIC_HOME/tools/git/pr-ci-wait.sh"
|
||||
expect_file "$MOSAIC_HOME/tools/orchestrator-matrix/transport/matrix_transport.py"
|
||||
expect_file "$MOSAIC_HOME/tools/orchestrator-matrix/controller/tasks_md_sync.py"
|
||||
expect_file "$MOSAIC_HOME/runtime/mcp/SEQUENTIAL-THINKING.json"
|
||||
expect_file "$MOSAIC_HOME/runtime/claude/RUNTIME.md"
|
||||
expect_file "$MOSAIC_HOME/runtime/codex/RUNTIME.md"
|
||||
|
||||
@@ -138,9 +138,9 @@ Write-Host "[mosaic-doctor] Mosaic home: $MosaicHome"
|
||||
# Canonical Mosaic checks
|
||||
Expect-File (Join-Path $MosaicHome "STANDARDS.md")
|
||||
Expect-Dir (Join-Path $MosaicHome "guides")
|
||||
Expect-Dir (Join-Path $MosaicHome "rails")
|
||||
Expect-Dir (Join-Path $MosaicHome "rails\quality")
|
||||
Expect-Dir (Join-Path $MosaicHome "rails\orchestrator-matrix")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools\quality")
|
||||
Expect-Dir (Join-Path $MosaicHome "tools\orchestrator-matrix")
|
||||
Expect-Dir (Join-Path $MosaicHome "profiles")
|
||||
Expect-Dir (Join-Path $MosaicHome "templates\agent")
|
||||
Expect-Dir (Join-Path $MosaicHome "skills")
|
||||
@@ -157,11 +157,11 @@ Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-drain")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-publish")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-consume")
|
||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-cycle")
|
||||
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.ps1")
|
||||
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "rails\git\pr-ci-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "rails\orchestrator-matrix\transport\matrix_transport.py")
|
||||
Expect-File (Join-Path $MosaicHome "rails\orchestrator-matrix\controller\tasks_md_sync.py")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.ps1")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "tools\git\pr-ci-wait.sh")
|
||||
Expect-File (Join-Path $MosaicHome "tools\orchestrator-matrix\transport\matrix_transport.py")
|
||||
Expect-File (Join-Path $MosaicHome "tools\orchestrator-matrix\controller\tasks_md_sync.py")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\mcp\SEQUENTIAL-THINKING.json")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\claude\RUNTIME.md")
|
||||
Expect-File (Join-Path $MosaicHome "runtime\codex\RUNTIME.md")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
BRIDGE="$MOSAIC_HOME/rails/orchestrator-matrix/transport/matrix_transport.py"
|
||||
BRIDGE="$MOSAIC_HOME/tools/orchestrator-matrix/transport/matrix_transport.py"
|
||||
|
||||
if [[ ! -f "$BRIDGE" ]]; then
|
||||
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
BRIDGE="$MOSAIC_HOME/rails/orchestrator-matrix/transport/matrix_transport.py"
|
||||
BRIDGE="$MOSAIC_HOME/tools/orchestrator-matrix/transport/matrix_transport.py"
|
||||
|
||||
if [[ ! -f "$BRIDGE" ]]; then
|
||||
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
CTRL="$MOSAIC_HOME/rails/orchestrator-matrix/controller/mosaic_orchestrator.py"
|
||||
CTRL="$MOSAIC_HOME/tools/orchestrator-matrix/controller/mosaic_orchestrator.py"
|
||||
|
||||
if [[ ! -f "$CTRL" ]]; then
|
||||
echo "[mosaic-orchestrator] missing controller: $CTRL" >&2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SYNC="$MOSAIC_HOME/rails/orchestrator-matrix/controller/tasks_md_sync.py"
|
||||
SYNC="$MOSAIC_HOME/tools/orchestrator-matrix/controller/tasks_md_sync.py"
|
||||
|
||||
if [[ ! -f "$SYNC" ]]; then
|
||||
echo "[mosaic-orchestrator-sync] missing sync script: $SYNC" >&2
|
||||
|
||||
@@ -9,7 +9,7 @@ usage() {
|
||||
cat <<USAGE
|
||||
Usage: $(basename "$0") --template <name> [--target <dir>]
|
||||
|
||||
Apply Mosaic quality rails templates into a project.
|
||||
Apply Mosaic quality tools templates into a project.
|
||||
|
||||
Templates:
|
||||
typescript-node
|
||||
@@ -55,7 +55,7 @@ if [[ ! -d "$TARGET_DIR" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT="$MOSAIC_HOME/rails/quality/scripts/install.sh"
|
||||
SCRIPT="$MOSAIC_HOME/tools/quality/scripts/install.sh"
|
||||
if [[ ! -x "$SCRIPT" ]]; then
|
||||
echo "[mosaic-quality] Missing install script: $SCRIPT" >&2
|
||||
exit 1
|
||||
|
||||
@@ -39,7 +39,7 @@ if [[ ! -d "$TARGET_DIR" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT="$MOSAIC_HOME/rails/quality/scripts/verify.sh"
|
||||
SCRIPT="$MOSAIC_HOME/tools/quality/scripts/verify.sh"
|
||||
if [[ ! -x "$SCRIPT" ]]; then
|
||||
echo "[mosaic-quality] Missing verify script: $SCRIPT" >&2
|
||||
exit 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Authentication & Authorization Guide
|
||||
|
||||
## Before Starting
|
||||
1. Check assigned issue: `~/.config/mosaic/rails/git/issue-list.sh -a @me`
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Review existing auth implementation in codebase
|
||||
3. Review Vault secrets structure: `docs/vault-secrets-structure.md`
|
||||
|
||||
@@ -115,6 +115,41 @@ class TestAuthentication:
|
||||
pass
|
||||
```
|
||||
|
||||
## Authentik SSO Administration
|
||||
|
||||
Authentik is the identity provider for the Mosaic Stack. Use the Authentik tool suite for administration.
|
||||
|
||||
### Tool Suite
|
||||
|
||||
```bash
|
||||
# System health
|
||||
~/.config/mosaic/tools/authentik/admin-status.sh
|
||||
|
||||
# User management
|
||||
~/.config/mosaic/tools/authentik/user-list.sh
|
||||
~/.config/mosaic/tools/authentik/user-create.sh -u <username> -n <name> -e <email>
|
||||
|
||||
# Group and app management
|
||||
~/.config/mosaic/tools/authentik/group-list.sh
|
||||
~/.config/mosaic/tools/authentik/app-list.sh
|
||||
~/.config/mosaic/tools/authentik/flow-list.sh
|
||||
```
|
||||
|
||||
### Registering an OAuth Application
|
||||
|
||||
1. Create an OAuth2 provider in Authentik admin (Applications > Providers)
|
||||
2. Create an application linked to the provider (Applications > Applications)
|
||||
3. Configure redirect URIs for the application
|
||||
4. Store client_id and client_secret in Vault: `secret-{env}/{service}/oauth/authentik/`
|
||||
5. Verify with: `~/.config/mosaic/tools/authentik/app-list.sh`
|
||||
|
||||
### API Reference
|
||||
|
||||
- Base URL: `https://auth.diversecanvas.com`
|
||||
- API prefix: `/api/v3/`
|
||||
- OpenAPI schema: `/api/v3/schema/`
|
||||
- Auth: Bearer token (obtained via `auth-token.sh`)
|
||||
|
||||
## Common Vulnerabilities to Avoid
|
||||
|
||||
1. **Broken Authentication**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Backend Development Guide
|
||||
|
||||
## Before Starting
|
||||
1. Check assigned issue: `~/.config/mosaic/rails/git/issue-list.sh -a @me`
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review API contracts and database schema
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ This guide covers how to bootstrap a project so AI agents (Claude, Codex, etc.)
|
||||
|
||||
```bash
|
||||
# Automated bootstrap (recommended)
|
||||
~/.config/mosaic/rails/bootstrap/init-project.sh \
|
||||
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||
--name "my-project" \
|
||||
--type "nestjs-nextjs" \
|
||||
--repo "https://git.mosaicstack.dev/owner/repo"
|
||||
@@ -240,10 +240,10 @@ Documentation root hygiene (HARD RULE):
|
||||
|
||||
```bash
|
||||
# Use the init script
|
||||
~/.config/mosaic/rails/bootstrap/init-repo-labels.sh
|
||||
~/.config/mosaic/tools/bootstrap/init-repo-labels.sh
|
||||
|
||||
# Or manually create standard labels
|
||||
~/.config/mosaic/rails/git/issue-create.sh # (labels are created on first use)
|
||||
~/.config/mosaic/tools/git/issue-create.sh # (labels are created on first use)
|
||||
```
|
||||
|
||||
### Standard Labels
|
||||
@@ -264,10 +264,10 @@ Create the first pre-MVP milestone at `0.0.1`.
|
||||
Reserve `0.1.0` for the MVP release milestone.
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/git/milestone-create.sh -t "0.0.1" -d "Pre-MVP - Foundation Sprint"
|
||||
~/.config/mosaic/tools/git/milestone-create.sh -t "0.0.1" -d "Pre-MVP - Foundation Sprint"
|
||||
|
||||
# Create when MVP scope is complete and release-ready:
|
||||
~/.config/mosaic/rails/git/milestone-create.sh -t "0.1.0" -d "MVP - Minimum Viable Product"
|
||||
~/.config/mosaic/tools/git/milestone-create.sh -t "0.1.0" -d "MVP - Minimum Viable Product"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -293,8 +293,8 @@ This enforces one merge strategy across human and agent workflows.
|
||||
```bash
|
||||
# Copy Codex review pipeline
|
||||
mkdir -p .woodpecker/schemas
|
||||
cp ~/.config/mosaic/rails/codex/woodpecker/codex-review.yml .woodpecker/
|
||||
cp ~/.config/mosaic/rails/codex/schemas/*.json .woodpecker/schemas/
|
||||
cp ~/.config/mosaic/tools/codex/woodpecker/codex-review.yml .woodpecker/
|
||||
cp ~/.config/mosaic/tools/codex/schemas/*.json .woodpecker/schemas/
|
||||
|
||||
# Add codex_api_key secret to Woodpecker CI dashboard
|
||||
```
|
||||
@@ -366,7 +366,7 @@ fi
|
||||
# (execute the command block under "Quality Gates")
|
||||
|
||||
# Test Codex review (if configured)
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh --help
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --help
|
||||
|
||||
# Verify sequential-thinking MCP remains configured
|
||||
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking --check
|
||||
@@ -434,7 +434,7 @@ fi
|
||||
Full project bootstrap with interactive and flag-based modes:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/bootstrap/init-project.sh \
|
||||
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||
--name "My Project" \
|
||||
--type "nestjs-nextjs" \
|
||||
--repo "https://git.mosaicstack.dev/owner/repo" \
|
||||
@@ -447,7 +447,7 @@ Full project bootstrap with interactive and flag-based modes:
|
||||
Initialize standard labels and the first pre-MVP milestone:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/bootstrap/init-repo-labels.sh
|
||||
~/.config/mosaic/tools/bootstrap/init-repo-labels.sh
|
||||
```
|
||||
|
||||
---
|
||||
@@ -483,4 +483,4 @@ After bootstrapping, verify:
|
||||
- [ ] `.env.example` exists (if project uses env vars)
|
||||
- [ ] CI/CD pipeline configured (if using Woodpecker/GitHub Actions)
|
||||
- [ ] Python publish path configured in CI (if project ships Python packages)
|
||||
- [ ] Codex review scripts accessible (`~/.config/mosaic/rails/codex/`)
|
||||
- [ ] Codex review scripts accessible (`~/.config/mosaic/tools/codex/`)
|
||||
|
||||
@@ -12,7 +12,7 @@ Merge strategy enforcement (HARD RULE):
|
||||
- PR target for delivery is `main`.
|
||||
- Direct pushes to `main` are prohibited.
|
||||
- Merge to `main` MUST be squash-only.
|
||||
- Use `~/.config/mosaic/rails/git/pr-merge.sh -n {PR_NUMBER} -m squash` (or PowerShell equivalent).
|
||||
- Use `~/.config/mosaic/tools/git/pr-merge.sh -n {PR_NUMBER} -m squash` (or PowerShell equivalent).
|
||||
|
||||
## Review Checklist
|
||||
|
||||
@@ -101,7 +101,7 @@ Use `~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md` whenever code/A
|
||||
### Getting Context
|
||||
```bash
|
||||
# List the issue being addressed
|
||||
~/.config/mosaic/rails/git/issue-list.sh -i {issue-number}
|
||||
~/.config/mosaic/tools/git/issue-list.sh -i {issue-number}
|
||||
|
||||
# View the changes
|
||||
git diff main...HEAD
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Frontend Development Guide
|
||||
|
||||
## Before Starting
|
||||
1. Check assigned issue in git repo: `~/.config/mosaic/rails/git/issue-list.sh -a @me`
|
||||
1. Check assigned issue in git repo: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review existing components and patterns in the codebase
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Infrastructure & DevOps Guide
|
||||
|
||||
## Before Starting
|
||||
1. Check assigned issue: `~/.config/mosaic/rails/git/issue-list.sh -a @me`
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review existing infrastructure configuration
|
||||
|
||||
@@ -125,11 +125,42 @@ The human is escalation-only for missing access, hard policy conflicts, or irrev
|
||||
|
||||
### Supported Targets
|
||||
|
||||
- **Portainer**: Deploy via configured stack webhook/API, then verify service health and container status.
|
||||
- **Coolify**: Trigger deployment via Coolify API/webhook, then verify deployment status and endpoint health.
|
||||
- **Portainer**: Deploy via `~/.config/mosaic/tools/portainer/stack-redeploy.sh`, then verify with `stack-status.sh`.
|
||||
- **Coolify**: Deploy via `~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>`, then verify with `service-status.sh`.
|
||||
- **Vercel**: Deploy via `vercel` CLI or connected Git integration, then verify preview/production URL health.
|
||||
- **Other SaaS providers**: Use provider CLI/API/runbook with the same validation and rollback gates.
|
||||
|
||||
### Coolify API Operations
|
||||
|
||||
```bash
|
||||
# List projects and services
|
||||
~/.config/mosaic/tools/coolify/project-list.sh
|
||||
~/.config/mosaic/tools/coolify/service-list.sh
|
||||
|
||||
# Check service status
|
||||
~/.config/mosaic/tools/coolify/service-status.sh -u <uuid>
|
||||
|
||||
# Set env vars (takes effect on next deploy)
|
||||
~/.config/mosaic/tools/coolify/env-set.sh -u <uuid> -k KEY -v VALUE
|
||||
|
||||
# Deploy
|
||||
~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>
|
||||
```
|
||||
|
||||
**Known Coolify Limitations:**
|
||||
- FQDN updates on compose sub-apps not supported via API (DB workaround required)
|
||||
- Compose files must be base64-encoded in `docker_compose_raw` field
|
||||
- Magic variables (`SERVICE_FQDN_*`) require list-style env syntax, not dict-style
|
||||
- Rate limit: 200 requests per interval
|
||||
|
||||
### Stack Health Check
|
||||
|
||||
Verify all infrastructure services are reachable:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/tools/health/stack-health.sh
|
||||
```
|
||||
|
||||
### Image Tagging and Promotion (Hard Rule)
|
||||
|
||||
For containerized deployments:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Before Starting
|
||||
|
||||
1. Check assigned issue: `~/.config/mosaic/rails/git/issue-list.sh -a @me`
|
||||
1. Check assigned issue: `~/.config/mosaic/tools/git/issue-list.sh -a @me`
|
||||
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||
3. Review `docs/PRD.md` or `docs/PRD.json` as the requirements source.
|
||||
4. Review acceptance criteria and affected change surfaces.
|
||||
|
||||
@@ -870,7 +870,7 @@ Required sequence:
|
||||
1. Merge PR to `main` (squash) via Mosaic wrapper.
|
||||
2. Monitor CI to terminal status:
|
||||
```bash
|
||||
~/.config/mosaic/rails/git/pr-ci-wait.sh -n <PR_NUMBER>
|
||||
~/.config/mosaic/tools/git/pr-ci-wait.sh -n <PR_NUMBER>
|
||||
```
|
||||
3. Require green status before claiming completion.
|
||||
4. If CI fails, create remediation task(s) and continue until green.
|
||||
@@ -885,8 +885,8 @@ Woodpecker note:
|
||||
Before pushing a branch or merging a PR, guard against overlapping project pipelines:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push -B main
|
||||
~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose merge -B main
|
||||
~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push -B main
|
||||
~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge -B main
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
@@ -25,8 +25,8 @@ First response MUST declare mode before tool calls or implementation steps:
|
||||
|
||||
1. For non-trivial work, `docs/TASKS.md` MUST exist before coding.
|
||||
2. If `docs/TASKS.md` is missing, create it from `~/.config/mosaic/templates/docs/TASKS.md.template`.
|
||||
3. Detect provider first via `~/.config/mosaic/rails/git/detect-platform.sh`.
|
||||
4. For issue/PR/milestone operations, use Mosaic wrappers first (`~/.config/mosaic/rails/git/*.sh`).
|
||||
3. Detect provider first via `~/.config/mosaic/tools/git/detect-platform.sh`.
|
||||
4. For issue/PR/milestone operations, use Mosaic wrappers first (`~/.config/mosaic/tools/git/*.sh`).
|
||||
5. If external git provider is available (Gitea/GitHub/GitLab), create or update issue(s) before coding.
|
||||
6. Record provider issue reference(s) in `docs/TASKS.md` (example: `#123`).
|
||||
7. If no external provider is available, use internal task refs in `docs/TASKS.md` (example: `TASKS:T1`).
|
||||
@@ -73,11 +73,11 @@ For implementation work, you MUST run this cycle in order:
|
||||
5. `remediate` - fix all findings and any test failures.
|
||||
6. `review` - re-review remediated changes until blockers are cleared.
|
||||
7. `commit` - commit only when the logical unit passes tests and review.
|
||||
8. `pre-push queue guard` - before pushing, wait for running/queued project pipelines to clear: `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push`.
|
||||
8. `pre-push queue guard` - before pushing, wait for running/queued project pipelines to clear: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push`.
|
||||
9. `push` - push immediately after queue guard passes.
|
||||
10. `PR integration` - if external git provider is available, create/update PR to `main` and merge with required strategy via Mosaic wrappers.
|
||||
11. `pre-merge queue guard` - before merging PR, wait for running/queued project pipelines to clear: `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose merge`.
|
||||
12. `CI/pipeline verification` - wait for terminal CI status and require green before completion (`~/.config/mosaic/rails/git/pr-ci-wait.sh` for PR-based workflow).
|
||||
11. `pre-merge queue guard` - before merging PR, wait for running/queued project pipelines to clear: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge`.
|
||||
12. `CI/pipeline verification` - wait for terminal CI status and require green before completion (`~/.config/mosaic/tools/git/pr-ci-wait.sh` for PR-based workflow).
|
||||
13. `issue closure` - close linked external issue (or close internal `docs/TASKS.md` task ref when provider is unavailable).
|
||||
14. `greenfield situational test` - validate required user flows in a clean environment/startup path (post-merge for trunk workflow changes).
|
||||
15. `deploy + post-deploy validation` - when deployment is in scope, deploy to configured target and run post-deploy health/smoke checks.
|
||||
@@ -85,10 +85,10 @@ For implementation work, you MUST run this cycle in order:
|
||||
|
||||
### Post-PR Hard Gate (Execute Sequentially, No Exceptions)
|
||||
|
||||
1. `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose merge -B main`
|
||||
2. `~/.config/mosaic/rails/git/pr-merge.sh -n <PR_NUMBER> -m squash`
|
||||
3. `~/.config/mosaic/rails/git/pr-ci-wait.sh -n <PR_NUMBER>`
|
||||
4. `~/.config/mosaic/rails/git/issue-close.sh -i <ISSUE_NUMBER>` (or close internal `docs/TASKS.md` ref when no provider exists)
|
||||
1. `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge -B main`
|
||||
2. `~/.config/mosaic/tools/git/pr-merge.sh -n <PR_NUMBER> -m squash`
|
||||
3. `~/.config/mosaic/tools/git/pr-ci-wait.sh -n <PR_NUMBER>`
|
||||
4. `~/.config/mosaic/tools/git/issue-close.sh -i <ISSUE_NUMBER>` (or close internal `docs/TASKS.md` ref when no provider exists)
|
||||
5. If any step fails: set status `blocked`, report the exact failed wrapper command, and stop.
|
||||
6. Do not ask the human to perform routine merge/close operations.
|
||||
7. Do not claim completion before step 4 succeeds.
|
||||
|
||||
@@ -272,7 +272,7 @@ Provider options:
|
||||
1. Gitea (preferred when available) via Mosaic helper:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/git/issue-create.sh \
|
||||
~/.config/mosaic/tools/git/issue-create.sh \
|
||||
-t "Phase 1: Critical Security Fixes" \
|
||||
-b "$(cat <<'EOF'
|
||||
## Findings
|
||||
@@ -412,15 +412,15 @@ git push
|
||||
and checklist completed (`~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md`) when applicable.
|
||||
13. **PR + CI + Issue Closure Gate** (HARD RULE for source-code tasks):
|
||||
- Before merging, run queue guard:
|
||||
`~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose merge -B main`
|
||||
`~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge -B main`
|
||||
- Ensure PR exists for the task branch (create/update via wrappers if needed):
|
||||
`~/.config/mosaic/rails/git/pr-create.sh ... -B main`
|
||||
`~/.config/mosaic/tools/git/pr-create.sh ... -B main`
|
||||
- Merge via wrapper:
|
||||
`~/.config/mosaic/rails/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
||||
`~/.config/mosaic/tools/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
||||
- Wait for terminal CI status:
|
||||
`~/.config/mosaic/rails/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
||||
`~/.config/mosaic/tools/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
||||
- Close linked issue after merge + green CI:
|
||||
`~/.config/mosaic/rails/git/issue-close.sh -i {ISSUE_NUMBER}`
|
||||
`~/.config/mosaic/tools/git/issue-close.sh -i {ISSUE_NUMBER}`
|
||||
- If any wrapper command fails, mark task `blocked`, record the exact failed wrapper command, report blocker, and STOP.
|
||||
- Do NOT stop at "PR created" or "PR merged pending CI".
|
||||
- Do NOT claim completion before CI is green and issue/internal ref is closed.
|
||||
@@ -463,10 +463,10 @@ Run review when the worker's result includes code changes (commits). Skip for ta
|
||||
cd {project_path}
|
||||
|
||||
# Code quality review
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh -b {base_branch} -o /tmp/review-{task_id}.json
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh -b {base_branch} -o /tmp/review-{task_id}.json
|
||||
|
||||
# Security review
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh -b {base_branch} -o /tmp/security-{task_id}.json
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh -b {base_branch} -o /tmp/security-{task_id}.json
|
||||
```
|
||||
|
||||
### Step 2: Parse Review Results
|
||||
@@ -599,19 +599,19 @@ Construct this from the task row and pass to worker via Task tool:
|
||||
7. If task is bug fix/security/auth/critical business logic, apply REQUIRED TDD discipline per `~/.config/mosaic/guides/QA-TESTING.md`.
|
||||
8. If gates or required situational tests fail: Fix and retry. Do NOT report success with failures.
|
||||
9. Commit: `git commit -m "fix({finding_id}): brief description"`
|
||||
10. Before push, run queue guard: `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push -B main`
|
||||
10. Before push, run queue guard: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push -B main`
|
||||
11. Push: `git push origin {branch}`
|
||||
12. Report result as JSON (see format below)
|
||||
|
||||
## Git Scripts
|
||||
|
||||
For issue/PR/milestone operations, use scripts (NOT raw tea/gh):
|
||||
- `~/.config/mosaic/rails/git/issue-view.sh -i {N}`
|
||||
- `~/.config/mosaic/rails/git/pr-create.sh -t "Title" -b "Desc" -B main`
|
||||
- `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push|merge -B main`
|
||||
- `~/.config/mosaic/rails/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
||||
- `~/.config/mosaic/rails/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
||||
- `~/.config/mosaic/rails/git/issue-close.sh -i {N}`
|
||||
- `~/.config/mosaic/tools/git/issue-view.sh -i {N}`
|
||||
- `~/.config/mosaic/tools/git/pr-create.sh -t "Title" -b "Desc" -B main`
|
||||
- `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge -B main`
|
||||
- `~/.config/mosaic/tools/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
||||
- `~/.config/mosaic/tools/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
||||
- `~/.config/mosaic/tools/git/issue-close.sh -i {N}`
|
||||
|
||||
Standard git commands (pull, commit, push, checkout) are fine.
|
||||
|
||||
@@ -1035,7 +1035,7 @@ When all tasks in `docs/TASKS.md` are `done` (or triaged as `deferred`), you MUS
|
||||
5. **Close milestone in provider**:
|
||||
- Gitea/GitHub:
|
||||
```bash
|
||||
~/.config/mosaic/rails/git/milestone-close.sh -t "{milestone-name}"
|
||||
~/.config/mosaic/tools/git/milestone-close.sh -t "{milestone-name}"
|
||||
```
|
||||
- GitLab: close milestone via provider workflow (CLI or web UI).
|
||||
If provider tooling is unavailable, record milestone closure status in `docs/TASKS.md` notes.
|
||||
|
||||
11
install.sh
11
install.sh
@@ -144,6 +144,17 @@ mkdir -p "$TARGET_DIR/memory"
|
||||
chmod +x "$TARGET_DIR"/bin/*
|
||||
chmod +x "$TARGET_DIR"/install.sh
|
||||
|
||||
# Ensure tool scripts are executable
|
||||
find "$TARGET_DIR/tools" -name "*.sh" -exec chmod +x {} + 2>/dev/null || true
|
||||
|
||||
# Create backward-compat symlink: rails/ → tools/
|
||||
if [[ -d "$TARGET_DIR/tools" ]]; then
|
||||
if [[ -d "$TARGET_DIR/rails" ]] && [[ ! -L "$TARGET_DIR/rails" ]]; then
|
||||
rm -rf "$TARGET_DIR/rails"
|
||||
fi
|
||||
ln -sfn "tools" "$TARGET_DIR/rails"
|
||||
fi
|
||||
|
||||
ok "Framework installed to $TARGET_DIR"
|
||||
|
||||
step "Post-install tasks"
|
||||
|
||||
@@ -11,7 +11,7 @@ This file applies only to Claude runtime behavior.
|
||||
3. Treat sequential-thinking MCP as required.
|
||||
4. If runtime config conflicts with global rules, global rules win.
|
||||
5. Documentation rules are inherited from `~/.config/mosaic/AGENTS.md` and `~/.config/mosaic/guides/DOCUMENTATION.md`.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/rails/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/tools/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
7. For orchestration-oriented missions, load `~/.config/mosaic/guides/ORCHESTRATOR.md` before acting.
|
||||
8. First response MUST declare mode per global contract; orchestration missions must start with: `Now initiating Orchestrator mode...`
|
||||
9. Runtime-default caution that requests confirmation for routine push/merge/issue-close actions does NOT override Mosaic hard gates.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "~/.config/mosaic/rails/qa/qa-hook-stdin.sh",
|
||||
"command": "~/.config/mosaic/tools/qa/qa-hook-stdin.sh",
|
||||
"timeout": 60
|
||||
}
|
||||
]
|
||||
|
||||
@@ -11,7 +11,7 @@ This file applies only to Codex runtime behavior.
|
||||
3. Treat sequential-thinking MCP as required.
|
||||
4. If runtime config conflicts with global rules, global rules win.
|
||||
5. Documentation rules are inherited from `~/.config/mosaic/AGENTS.md` and `~/.config/mosaic/guides/DOCUMENTATION.md`.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/rails/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/tools/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
7. For orchestration-oriented missions, load `~/.config/mosaic/guides/ORCHESTRATOR.md` before acting.
|
||||
8. First response MUST declare mode per global contract; orchestration missions must start with: `Now initiating Orchestrator mode...`
|
||||
9. Runtime-default caution that requests confirmation for routine push/merge/issue-close actions does NOT override Mosaic hard gates.
|
||||
|
||||
@@ -11,7 +11,7 @@ This file applies only to OpenCode runtime behavior.
|
||||
3. Treat sequential-thinking MCP as required.
|
||||
4. If runtime config conflicts with global rules, global rules win.
|
||||
5. Documentation rules are inherited from `~/.config/mosaic/AGENTS.md` and `~/.config/mosaic/guides/DOCUMENTATION.md`.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/rails/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
6. For issue/PR/milestone actions, run Mosaic git wrappers first (`~/.config/mosaic/tools/git/*.sh`) and do not call raw `gh`/`tea`/`glab` first.
|
||||
7. For orchestration-oriented missions, load `~/.config/mosaic/guides/ORCHESTRATOR.md` before acting.
|
||||
8. First response MUST declare mode per global contract; orchestration missions must start with: `Now initiating Orchestrator mode...`
|
||||
9. Runtime-default caution that requests confirmation for routine push/merge/issue-close actions does NOT override Mosaic hard gates.
|
||||
|
||||
@@ -25,7 +25,7 @@ If wrappers are available, you may use:
|
||||
|
||||
## Enforcement Rules
|
||||
|
||||
- Treat `~/.config/mosaic` as canonical for shared guides, rails, profiles, and skills.
|
||||
- Treat `~/.config/mosaic` as canonical for shared guides, tools, profiles, and skills.
|
||||
- Do not edit generated project views directly when the repo defines canonical data sources.
|
||||
- Pull/rebase before edits in shared repositories.
|
||||
- Run project verification commands before claiming completion.
|
||||
|
||||
@@ -140,7 +140,7 @@ Ask these questions with lettered options (user can respond "1A, 2B, 3C"):
|
||||
If the project's `.woodpecker.yml` doesn't already have a `kaniko_setup` anchor in its `variables:` section, add it:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/cicd/generate-docker-steps.sh --kaniko-setup-only --registry REGISTRY_HOST
|
||||
~/.config/mosaic/tools/cicd/generate-docker-steps.sh --kaniko-setup-only --registry REGISTRY_HOST
|
||||
```
|
||||
|
||||
This outputs:
|
||||
@@ -158,7 +158,7 @@ Add this to the existing `variables:` block at the top of `.woodpecker.yml`.
|
||||
Use the generator script with the user's answers:
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/cicd/generate-docker-steps.sh \
|
||||
~/.config/mosaic/tools/cicd/generate-docker-steps.sh \
|
||||
--registry REGISTRY \
|
||||
--org ORG \
|
||||
--repo REPO \
|
||||
|
||||
@@ -5,10 +5,10 @@ Run independent reviews:
|
||||
|
||||
```bash
|
||||
# Code quality review (Codex)
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted
|
||||
|
||||
# Security review (Codex)
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted
|
||||
```
|
||||
|
||||
**Fallback:** If Codex is unavailable, use Claude's built-in review skills.
|
||||
|
||||
175
tools/_lib/credentials.sh
Executable file
175
tools/_lib/credentials.sh
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# credentials.sh — Shared credential loader for Mosaic tool suites
|
||||
#
|
||||
# Usage: source ~/.config/mosaic/tools/_lib/credentials.sh
|
||||
# load_credentials <service-name>
|
||||
#
|
||||
# Loads credentials from environment variables first, then falls back
|
||||
# to ~/src/jarvis-brain/credentials.json (or MOSAIC_CREDENTIALS_FILE).
|
||||
#
|
||||
# Supported services:
|
||||
# portainer, coolify, authentik, glpi, github,
|
||||
# gitea-mosaicstack, gitea-usc, woodpecker
|
||||
#
|
||||
# After loading, service-specific env vars are exported.
|
||||
# Run `load_credentials --help` for details.
|
||||
|
||||
MOSAIC_CREDENTIALS_FILE="${MOSAIC_CREDENTIALS_FILE:-$HOME/src/jarvis-brain/credentials.json}"
|
||||
|
||||
_mosaic_require_jq() {
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "Error: jq is required but not installed" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_mosaic_read_cred() {
|
||||
local jq_path="$1"
|
||||
if [[ ! -f "$MOSAIC_CREDENTIALS_FILE" ]]; then
|
||||
echo "Error: Credentials file not found: $MOSAIC_CREDENTIALS_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
jq -r "$jq_path // empty" "$MOSAIC_CREDENTIALS_FILE"
|
||||
}
|
||||
|
||||
load_credentials() {
|
||||
local service="$1"
|
||||
|
||||
if [[ -z "$service" || "$service" == "--help" ]]; then
|
||||
cat <<'EOF'
|
||||
Usage: load_credentials <service>
|
||||
|
||||
Services and exported variables:
|
||||
portainer → PORTAINER_URL, PORTAINER_API_KEY
|
||||
coolify → COOLIFY_URL, COOLIFY_TOKEN
|
||||
authentik → AUTHENTIK_URL, AUTHENTIK_TOKEN, AUTHENTIK_USERNAME, AUTHENTIK_PASSWORD
|
||||
glpi → GLPI_URL, GLPI_APP_TOKEN, GLPI_USER_TOKEN
|
||||
github → GITHUB_TOKEN
|
||||
gitea-mosaicstack → GITEA_URL, GITEA_TOKEN
|
||||
gitea-usc → GITEA_URL, GITEA_TOKEN
|
||||
woodpecker → WOODPECKER_URL, WOODPECKER_TOKEN
|
||||
EOF
|
||||
return 0
|
||||
fi
|
||||
|
||||
_mosaic_require_jq || return 1
|
||||
|
||||
case "$service" in
|
||||
portainer)
|
||||
export PORTAINER_URL="${PORTAINER_URL:-$(_mosaic_read_cred '.portainer.url')}"
|
||||
export PORTAINER_API_KEY="${PORTAINER_API_KEY:-$(_mosaic_read_cred '.portainer.api_key')}"
|
||||
PORTAINER_URL="${PORTAINER_URL%/}"
|
||||
[[ -n "$PORTAINER_URL" ]] || { echo "Error: portainer.url not found" >&2; return 1; }
|
||||
[[ -n "$PORTAINER_API_KEY" ]] || { echo "Error: portainer.api_key not found" >&2; return 1; }
|
||||
;;
|
||||
coolify)
|
||||
export COOLIFY_URL="${COOLIFY_URL:-$(_mosaic_read_cred '.coolify.url')}"
|
||||
export COOLIFY_TOKEN="${COOLIFY_TOKEN:-$(_mosaic_read_cred '.coolify.app_token')}"
|
||||
COOLIFY_URL="${COOLIFY_URL%/}"
|
||||
[[ -n "$COOLIFY_URL" ]] || { echo "Error: coolify.url not found" >&2; return 1; }
|
||||
[[ -n "$COOLIFY_TOKEN" ]] || { echo "Error: coolify.app_token not found" >&2; return 1; }
|
||||
;;
|
||||
authentik)
|
||||
export AUTHENTIK_URL="${AUTHENTIK_URL:-$(_mosaic_read_cred '.authentik.url')}"
|
||||
export AUTHENTIK_TOKEN="${AUTHENTIK_TOKEN:-$(_mosaic_read_cred '.authentik.token')}"
|
||||
export AUTHENTIK_USERNAME="${AUTHENTIK_USERNAME:-$(_mosaic_read_cred '.authentik.username')}"
|
||||
export AUTHENTIK_PASSWORD="${AUTHENTIK_PASSWORD:-$(_mosaic_read_cred '.authentik.password')}"
|
||||
AUTHENTIK_URL="${AUTHENTIK_URL%/}"
|
||||
[[ -n "$AUTHENTIK_URL" ]] || { echo "Error: authentik.url not found" >&2; return 1; }
|
||||
;;
|
||||
glpi)
|
||||
export GLPI_URL="${GLPI_URL:-$(_mosaic_read_cred '.glpi.url')}"
|
||||
export GLPI_APP_TOKEN="${GLPI_APP_TOKEN:-$(_mosaic_read_cred '.glpi.app_token')}"
|
||||
export GLPI_USER_TOKEN="${GLPI_USER_TOKEN:-$(_mosaic_read_cred '.glpi.user_token')}"
|
||||
GLPI_URL="${GLPI_URL%/}"
|
||||
[[ -n "$GLPI_URL" ]] || { echo "Error: glpi.url not found" >&2; return 1; }
|
||||
;;
|
||||
github)
|
||||
export GITHUB_TOKEN="${GITHUB_TOKEN:-$(_mosaic_read_cred '.github.token')}"
|
||||
[[ -n "$GITHUB_TOKEN" ]] || { echo "Error: github.token not found" >&2; return 1; }
|
||||
;;
|
||||
gitea-mosaicstack)
|
||||
export GITEA_URL="${GITEA_URL:-$(_mosaic_read_cred '.gitea.mosaicstack.url')}"
|
||||
export GITEA_TOKEN="${GITEA_TOKEN:-$(_mosaic_read_cred '.gitea.mosaicstack.token')}"
|
||||
GITEA_URL="${GITEA_URL%/}"
|
||||
[[ -n "$GITEA_URL" ]] || { echo "Error: gitea.mosaicstack.url not found" >&2; return 1; }
|
||||
[[ -n "$GITEA_TOKEN" ]] || { echo "Error: gitea.mosaicstack.token not found" >&2; return 1; }
|
||||
;;
|
||||
gitea-usc)
|
||||
export GITEA_URL="${GITEA_URL:-$(_mosaic_read_cred '.gitea.usc.url')}"
|
||||
export GITEA_TOKEN="${GITEA_TOKEN:-$(_mosaic_read_cred '.gitea.usc.token')}"
|
||||
GITEA_URL="${GITEA_URL%/}"
|
||||
[[ -n "$GITEA_URL" ]] || { echo "Error: gitea.usc.url not found" >&2; return 1; }
|
||||
[[ -n "$GITEA_TOKEN" ]] || { echo "Error: gitea.usc.token not found" >&2; return 1; }
|
||||
;;
|
||||
woodpecker)
|
||||
export WOODPECKER_URL="${WOODPECKER_URL:-$(_mosaic_read_cred '.woodpecker.url')}"
|
||||
export WOODPECKER_TOKEN="${WOODPECKER_TOKEN:-$(_mosaic_read_cred '.woodpecker.token')}"
|
||||
WOODPECKER_URL="${WOODPECKER_URL%/}"
|
||||
[[ -n "$WOODPECKER_URL" ]] || { echo "Error: woodpecker.url not found" >&2; return 1; }
|
||||
[[ -n "$WOODPECKER_TOKEN" ]] || { echo "Error: woodpecker.token not found" >&2; return 1; }
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown service '$service'" >&2
|
||||
echo "Supported: portainer, coolify, authentik, glpi, github, gitea-mosaicstack, gitea-usc, woodpecker" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Common HTTP helper — makes a curl request and separates body from status code
|
||||
# Usage: mosaic_http GET "/api/v1/endpoint" "Authorization: Bearer $TOKEN" [base_url]
|
||||
# Returns: body on stdout, sets MOSAIC_HTTP_CODE
|
||||
mosaic_http() {
|
||||
local method="$1"
|
||||
local endpoint="$2"
|
||||
local auth_header="$3"
|
||||
local base_url="${4:-}"
|
||||
|
||||
local response
|
||||
response=$(curl -sk -w "\n%{http_code}" -X "$method" \
|
||||
-H "$auth_header" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${base_url}${endpoint}")
|
||||
|
||||
MOSAIC_HTTP_CODE=$(echo "$response" | tail -n1)
|
||||
echo "$response" | sed '$d'
|
||||
}
|
||||
|
||||
# POST variant with body
|
||||
# Usage: mosaic_http_post "/api/v1/endpoint" "Authorization: Bearer $TOKEN" '{"key":"val"}' [base_url]
|
||||
mosaic_http_post() {
|
||||
local endpoint="$1"
|
||||
local auth_header="$2"
|
||||
local data="$3"
|
||||
local base_url="${4:-}"
|
||||
|
||||
local response
|
||||
response=$(curl -sk -w "\n%{http_code}" -X POST \
|
||||
-H "$auth_header" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$data" \
|
||||
"${base_url}${endpoint}")
|
||||
|
||||
MOSAIC_HTTP_CODE=$(echo "$response" | tail -n1)
|
||||
echo "$response" | sed '$d'
|
||||
}
|
||||
|
||||
# PATCH variant with body
|
||||
mosaic_http_patch() {
|
||||
local endpoint="$1"
|
||||
local auth_header="$2"
|
||||
local data="$3"
|
||||
local base_url="${4:-}"
|
||||
|
||||
local response
|
||||
response=$(curl -sk -w "\n%{http_code}" -X PATCH \
|
||||
-H "$auth_header" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$data" \
|
||||
"${base_url}${endpoint}")
|
||||
|
||||
MOSAIC_HTTP_CODE=$(echo "$response" | tail -n1)
|
||||
echo "$response" | sed '$d'
|
||||
}
|
||||
59
tools/authentik/README.md
Normal file
59
tools/authentik/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Authentik Tool Suite
|
||||
|
||||
Manage Authentik identity provider (SSO, users, groups, applications, flows) via CLI.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `jq` installed
|
||||
- Authentik credentials in `~/src/jarvis-brain/credentials.json` (or `$MOSAIC_CREDENTIALS_FILE`)
|
||||
- Required fields: `authentik.url`, `authentik.username`, `authentik.password`
|
||||
|
||||
## Authentication
|
||||
|
||||
Scripts use `auth-token.sh` to auto-authenticate via username/password and cache the API token at `~/.cache/mosaic/authentik-token`. The token is validated on each use and refreshed automatically when expired.
|
||||
|
||||
For better security, create a long-lived API token in Authentik admin (Directory > Tokens) and set `$AUTHENTIK_TOKEN` in your environment — the scripts will use it directly.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `auth-token.sh` | Authenticate and cache API token |
|
||||
| `user-list.sh` | List users (search, filter by group) |
|
||||
| `user-create.sh` | Create user with optional group assignment |
|
||||
| `group-list.sh` | List groups |
|
||||
| `app-list.sh` | List OAuth/SAML applications |
|
||||
| `flow-list.sh` | List authentication flows |
|
||||
| `admin-status.sh` | System health and version info |
|
||||
|
||||
## Common Options
|
||||
|
||||
All scripts support:
|
||||
- `-f json` — JSON output (default: table)
|
||||
- `-h` — Show help
|
||||
|
||||
## API Reference
|
||||
|
||||
- Base URL: `https://auth.diversecanvas.com`
|
||||
- API prefix: `/api/v3/`
|
||||
- OpenAPI schema: `/api/v3/schema/`
|
||||
- Auth: Bearer token in `Authorization` header
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# List all users
|
||||
~/.config/mosaic/tools/authentik/user-list.sh
|
||||
|
||||
# Search for a user
|
||||
~/.config/mosaic/tools/authentik/user-list.sh -s "jason"
|
||||
|
||||
# Create a user in the admins group
|
||||
~/.config/mosaic/tools/authentik/user-create.sh -u newuser -n "New User" -e new@example.com -g admins
|
||||
|
||||
# List OAuth applications as JSON
|
||||
~/.config/mosaic/tools/authentik/app-list.sh -f json
|
||||
|
||||
# Check system health
|
||||
~/.config/mosaic/tools/authentik/admin-status.sh
|
||||
```
|
||||
55
tools/authentik/admin-status.sh
Executable file
55
tools/authentik/admin-status.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# admin-status.sh — Authentik system health and version info
|
||||
#
|
||||
# Usage: admin-status.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}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
FORMAT="table"
|
||||
|
||||
while getopts "f:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
h) head -11 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/admin/system/")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to get system status (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Authentik System Status"
|
||||
echo "======================="
|
||||
echo "$body" | jq -r '
|
||||
" URL: \(.http_host // "unknown")\n" +
|
||||
" Version: \(.runtime.authentik_version // "unknown")\n" +
|
||||
" Python: \(.runtime.python_version // "unknown")\n" +
|
||||
" Workers: \(.runtime.gunicorn_workers // "unknown")\n" +
|
||||
" Build Hash: \(.runtime.build_hash // "unknown")\n" +
|
||||
" Embedded Outpost: \(.embedded_outpost_host // "unknown")"
|
||||
'
|
||||
62
tools/authentik/app-list.sh
Executable file
62
tools/authentik/app-list.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# app-list.sh — List Authentik applications
|
||||
#
|
||||
# Usage: app-list.sh [-f format] [-s search]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -s search Search by application name
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
FORMAT="table"
|
||||
SEARCH=""
|
||||
|
||||
while getopts "f:s:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
s) SEARCH="$OPTARG" ;;
|
||||
h) head -12 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-s search]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
PARAMS="ordering=name"
|
||||
[[ -n "$SEARCH" ]] && PARAMS="${PARAMS}&search=${SEARCH}"
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/applications/?${PARAMS}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list applications (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.results'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "NAME SLUG PROVIDER LAUNCH URL"
|
||||
echo "---------------------------- ---------------------------- ----------------- ----------------------------------------"
|
||||
echo "$body" | jq -r '.results[] | [
|
||||
.name,
|
||||
.slug,
|
||||
(.provider_obj.name // "none"),
|
||||
(.launch_url // "—")
|
||||
] | @tsv' | while IFS=$'\t' read -r name slug provider launch_url; do
|
||||
printf "%-28s %-28s %-17s %s\n" \
|
||||
"${name:0:28}" "${slug:0:28}" "${provider:0:17}" "$launch_url"
|
||||
done
|
||||
86
tools/authentik/auth-token.sh
Executable file
86
tools/authentik/auth-token.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# auth-token.sh — Obtain and cache Authentik API token
|
||||
#
|
||||
# Usage: auth-token.sh [-f] [-q]
|
||||
#
|
||||
# Returns a valid Authentik API token. Checks in order:
|
||||
# 1. Cached token at ~/.cache/mosaic/authentik-token (if valid)
|
||||
# 2. Pre-configured token from credentials.json (authentik.token)
|
||||
# 3. Fails with instructions to create a token in the admin UI
|
||||
#
|
||||
# Options:
|
||||
# -f Force re-validation (ignore cached token)
|
||||
# -q Quiet mode — only output the token
|
||||
# -h Show this help
|
||||
#
|
||||
# Environment variables (or credentials.json):
|
||||
# AUTHENTIK_URL — Authentik instance URL
|
||||
# AUTHENTIK_TOKEN — Pre-configured API token (recommended)
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
CACHE_DIR="$HOME/.cache/mosaic"
|
||||
CACHE_FILE="$CACHE_DIR/authentik-token"
|
||||
FORCE=false
|
||||
QUIET=false
|
||||
|
||||
while getopts "fqh" opt; do
|
||||
case $opt in
|
||||
f) FORCE=true ;;
|
||||
q) QUIET=true ;;
|
||||
h) head -20 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f] [-q]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
_validate_token() {
|
||||
local token="$1"
|
||||
local http_code
|
||||
http_code=$(curl -sk -o /dev/null -w "%{http_code}" \
|
||||
--connect-timeout 5 --max-time 10 \
|
||||
-H "Authorization: Bearer $token" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/users/me/")
|
||||
[[ "$http_code" == "200" ]]
|
||||
}
|
||||
|
||||
# 1. Check cached token
|
||||
if [[ "$FORCE" == "false" ]] && [[ -f "$CACHE_FILE" ]]; then
|
||||
cached_token=$(cat "$CACHE_FILE")
|
||||
if [[ -n "$cached_token" ]] && _validate_token "$cached_token"; then
|
||||
[[ "$QUIET" == "false" ]] && echo "Using cached token (valid)" >&2
|
||||
echo "$cached_token"
|
||||
exit 0
|
||||
fi
|
||||
[[ "$QUIET" == "false" ]] && echo "Cached token invalid, checking credentials..." >&2
|
||||
fi
|
||||
|
||||
# 2. Use pre-configured token from credentials.json
|
||||
if [[ -n "${AUTHENTIK_TOKEN:-}" ]]; then
|
||||
if _validate_token "$AUTHENTIK_TOKEN"; then
|
||||
# Cache it for faster future access
|
||||
mkdir -p "$CACHE_DIR"
|
||||
echo "$AUTHENTIK_TOKEN" > "$CACHE_FILE"
|
||||
chmod 600 "$CACHE_FILE"
|
||||
[[ "$QUIET" == "false" ]] && echo "Token validated and cached at $CACHE_FILE" >&2
|
||||
echo "$AUTHENTIK_TOKEN"
|
||||
exit 0
|
||||
else
|
||||
echo "Error: Pre-configured AUTHENTIK_TOKEN is invalid (API returned non-200)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. No token available
|
||||
echo "Error: No Authentik API token configured" >&2
|
||||
echo "" >&2
|
||||
echo "To create one:" >&2
|
||||
echo " 1. Log into Authentik admin: ${AUTHENTIK_URL}/if/admin/#/core/tokens" >&2
|
||||
echo " 2. Click 'Create' → set identifier (e.g., 'mosaic-agent')" >&2
|
||||
echo " 3. Select 'API Token' intent, uncheck 'Expiring'" >&2
|
||||
echo " 4. Copy the key and add to credentials.json:" >&2
|
||||
echo " jq '.authentik.token = \"<your-token>\"' credentials.json > tmp && mv tmp credentials.json" >&2
|
||||
exit 1
|
||||
62
tools/authentik/flow-list.sh
Executable file
62
tools/authentik/flow-list.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# flow-list.sh — List Authentik flows
|
||||
#
|
||||
# Usage: flow-list.sh [-f format] [-d designation]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -d designation Filter by designation (authentication, authorization, enrollment, etc.)
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
FORMAT="table"
|
||||
DESIGNATION=""
|
||||
|
||||
while getopts "f:d:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
d) DESIGNATION="$OPTARG" ;;
|
||||
h) head -13 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-d designation]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
PARAMS="ordering=slug"
|
||||
[[ -n "$DESIGNATION" ]] && PARAMS="${PARAMS}&designation=${DESIGNATION}"
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/flows/instances/?${PARAMS}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list flows (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.results'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "NAME SLUG DESIGNATION TITLE"
|
||||
echo "---------------------------- ---------------------------- ---------------- ----------------------------"
|
||||
echo "$body" | jq -r '.results[] | [
|
||||
.name,
|
||||
.slug,
|
||||
.designation,
|
||||
(.title // "—")
|
||||
] | @tsv' | while IFS=$'\t' read -r name slug designation title; do
|
||||
printf "%-28s %-28s %-16s %s\n" \
|
||||
"${name:0:28}" "${slug:0:28}" "$designation" "${title:0:28}"
|
||||
done
|
||||
61
tools/authentik/group-list.sh
Executable file
61
tools/authentik/group-list.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# group-list.sh — List Authentik groups
|
||||
#
|
||||
# Usage: group-list.sh [-f format] [-s search]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -s search Search by group name
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
FORMAT="table"
|
||||
SEARCH=""
|
||||
|
||||
while getopts "f:s:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
s) SEARCH="$OPTARG" ;;
|
||||
h) head -12 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-s search]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
PARAMS="ordering=name"
|
||||
[[ -n "$SEARCH" ]] && PARAMS="${PARAMS}&search=${SEARCH}"
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/groups/?${PARAMS}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list groups (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.results'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "NAME PK MEMBERS SUPERUSER"
|
||||
echo "---------------------------- ------------------------------------ ------- ---------"
|
||||
echo "$body" | jq -r '.results[] | [
|
||||
.name,
|
||||
.pk,
|
||||
(.users | length | tostring),
|
||||
(if .is_superuser then "yes" else "no" end)
|
||||
] | @tsv' | while IFS=$'\t' read -r name pk members superuser; do
|
||||
printf "%-28s %-36s %-7s %s\n" "${name:0:28}" "$pk" "$members" "$superuser"
|
||||
done
|
||||
93
tools/authentik/user-create.sh
Executable file
93
tools/authentik/user-create.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# user-create.sh — Create an Authentik user
|
||||
#
|
||||
# Usage: user-create.sh -u <username> -n <name> -e <email> [-p password] [-g group]
|
||||
#
|
||||
# Options:
|
||||
# -u username Username (required)
|
||||
# -n name Display name (required)
|
||||
# -e email Email address (required)
|
||||
# -p password Initial password (optional — user gets set-password flow if omitted)
|
||||
# -g group Group name to add user to (optional)
|
||||
# -f format Output format: table (default), json
|
||||
# -h Show this help
|
||||
#
|
||||
# Environment variables (or credentials.json):
|
||||
# AUTHENTIK_URL — Authentik instance URL
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
USERNAME="" NAME="" EMAIL="" PASSWORD="" GROUP="" FORMAT="table"
|
||||
|
||||
while getopts "u:n:e:p:g:f:h" opt; do
|
||||
case $opt in
|
||||
u) USERNAME="$OPTARG" ;;
|
||||
n) NAME="$OPTARG" ;;
|
||||
e) EMAIL="$OPTARG" ;;
|
||||
p) PASSWORD="$OPTARG" ;;
|
||||
g) GROUP="$OPTARG" ;;
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
h) head -18 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 -u <username> -n <name> -e <email> [-p password] [-g group]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$USERNAME" || -z "$NAME" || -z "$EMAIL" ]]; then
|
||||
echo "Error: -u username, -n name, and -e email are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
# Build user payload
|
||||
payload=$(jq -n \
|
||||
--arg username "$USERNAME" \
|
||||
--arg name "$NAME" \
|
||||
--arg email "$EMAIL" \
|
||||
'{username: $username, name: $name, email: $email, is_active: true}')
|
||||
|
||||
# Add password if provided
|
||||
if [[ -n "$PASSWORD" ]]; then
|
||||
payload=$(echo "$payload" | jq --arg pw "$PASSWORD" '. + {password: $pw}')
|
||||
fi
|
||||
|
||||
# Add to group if provided
|
||||
if [[ -n "$GROUP" ]]; then
|
||||
# Look up group PK by name
|
||||
group_response=$(curl -sk \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/groups/?search=${GROUP}")
|
||||
group_pk=$(echo "$group_response" | jq -r ".results[] | select(.name == \"$GROUP\") | .pk" | head -1)
|
||||
if [[ -n "$group_pk" ]]; then
|
||||
payload=$(echo "$payload" | jq --arg gk "$group_pk" '. + {groups: [$gk]}')
|
||||
else
|
||||
echo "Warning: Group '$GROUP' not found — creating user without group" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/users/")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "201" ]]; then
|
||||
echo "Error: Failed to create user (HTTP $http_code)" >&2
|
||||
echo "$body" | jq -r '.' 2>/dev/null >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
else
|
||||
echo "User created successfully:"
|
||||
echo "$body" | jq -r '" Username: \(.username)\n Name: \(.name)\n Email: \(.email)\n PK: \(.pk)"'
|
||||
fi
|
||||
72
tools/authentik/user-list.sh
Executable file
72
tools/authentik/user-list.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# user-list.sh — List Authentik users
|
||||
#
|
||||
# Usage: user-list.sh [-f format] [-s search] [-g group]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -s search Search term (matches username, name, email)
|
||||
# -g group Filter by group name
|
||||
# -h Show this help
|
||||
#
|
||||
# Environment variables (or credentials.json):
|
||||
# AUTHENTIK_URL — Authentik instance URL
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials authentik
|
||||
|
||||
FORMAT="table"
|
||||
SEARCH=""
|
||||
GROUP=""
|
||||
|
||||
while getopts "f:s:g:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
s) SEARCH="$OPTARG" ;;
|
||||
g) GROUP="$OPTARG" ;;
|
||||
h) head -14 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-s search] [-g group]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOKEN=$("$SCRIPT_DIR/auth-token.sh" -q)
|
||||
|
||||
# Build query params
|
||||
PARAMS="ordering=username"
|
||||
[[ -n "$SEARCH" ]] && PARAMS="${PARAMS}&search=${SEARCH}"
|
||||
[[ -n "$GROUP" ]] && PARAMS="${PARAMS}&groups_by_name=${GROUP}"
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
"${AUTHENTIK_URL}/api/v3/core/users/?${PARAMS}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list users (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.results'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Table output
|
||||
echo "USERNAME NAME EMAIL ACTIVE LAST LOGIN"
|
||||
echo "-------------------- ---------------------------- ---------------------------- ------ ----------"
|
||||
echo "$body" | jq -r '.results[] | [
|
||||
.username,
|
||||
.name,
|
||||
.email,
|
||||
(if .is_active then "yes" else "no" end),
|
||||
(.last_login // "never" | split("T")[0])
|
||||
] | @tsv' | while IFS=$'\t' read -r username name email active last_login; do
|
||||
printf "%-20s %-28s %-28s %-6s %s\n" \
|
||||
"${username:0:20}" "${name:0:28}" "${email:0:28}" "$active" "$last_login"
|
||||
done
|
||||
@@ -230,9 +230,9 @@ JSONEOF
|
||||
|
||||
if $FIX_HINT && ! $JSON_OUTPUT; then
|
||||
if [[ "$has_runtime" == "MISS" || "$has_agents" == "MISS" ]]; then
|
||||
echo " ${DIM}Fix: ~/.config/mosaic/rails/bootstrap/init-project.sh --name \"$name\" --type auto${NC}"
|
||||
echo " ${DIM}Fix: ~/.config/mosaic/tools/bootstrap/init-project.sh --name \"$name\" --type auto${NC}"
|
||||
elif [[ "$has_guides" == "MISS" ]]; then
|
||||
echo " ${DIM}Fix: ~/.config/mosaic/rails/bootstrap/agent-upgrade.sh $dir --section conditional-loading${NC}"
|
||||
echo " ${DIM}Fix: ~/.config/mosaic/tools/bootstrap/agent-upgrade.sh $dir --section conditional-loading${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -9,7 +9,7 @@ set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATE_DIR="$HOME/.config/mosaic/templates/agent"
|
||||
GIT_SCRIPT_DIR="$HOME/.config/mosaic/rails/git"
|
||||
GIT_SCRIPT_DIR="$HOME/.config/mosaic/tools/git"
|
||||
SEQUENTIAL_MCP_SCRIPT="$HOME/.config/mosaic/bin/mosaic-ensure-sequential-thinking"
|
||||
|
||||
# Defaults
|
||||
@@ -403,7 +403,7 @@ echo "Created docs/scratchpads/, docs/reports/*, docs/tasks/, docs/releases/, do
|
||||
|
||||
# Set up CI/CD pipeline
|
||||
if [[ "$SKIP_CI" != true ]]; then
|
||||
CODEX_DIR="$HOME/.config/mosaic/rails/codex"
|
||||
CODEX_DIR="$HOME/.config/mosaic/tools/codex"
|
||||
if [[ -d "$CODEX_DIR/woodpecker" ]]; then
|
||||
mkdir -p .woodpecker/schemas
|
||||
cp "$CODEX_DIR/woodpecker/codex-review.yml" .woodpecker/
|
||||
@@ -416,7 +416,7 @@ fi
|
||||
|
||||
# Generate Docker build/push/link pipeline steps
|
||||
if [[ "$CICD_DOCKER" == true ]]; then
|
||||
CICD_SCRIPT="$HOME/.config/mosaic/rails/cicd/generate-docker-steps.sh"
|
||||
CICD_SCRIPT="$HOME/.config/mosaic/tools/cicd/generate-docker-steps.sh"
|
||||
if [[ -x "$CICD_SCRIPT" ]]; then
|
||||
# Parse org and repo from git remote
|
||||
CICD_REGISTRY=""
|
||||
@@ -7,7 +7,7 @@
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GIT_SCRIPT_DIR="$HOME/.config/mosaic/rails/git"
|
||||
GIT_SCRIPT_DIR="$HOME/.config/mosaic/tools/git"
|
||||
source "$GIT_SCRIPT_DIR/detect-platform.sh"
|
||||
|
||||
SKIP_MILESTONE=false
|
||||
@@ -50,45 +50,45 @@ Security vulnerability review focusing on:
|
||||
|
||||
```bash
|
||||
# Code review
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted
|
||||
|
||||
# Security review
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted
|
||||
```
|
||||
|
||||
### Review a Pull Request
|
||||
|
||||
```bash
|
||||
# Review and post findings as a PR comment
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh -n 42
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh -n 42
|
||||
|
||||
# Security review and post to PR
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh -n 42
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh -n 42
|
||||
```
|
||||
|
||||
### Review Against Base Branch
|
||||
|
||||
```bash
|
||||
# Code review changes vs main
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh -b main
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh -b main
|
||||
|
||||
# Security review changes vs develop
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh -b develop
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh -b develop
|
||||
```
|
||||
|
||||
### Review a Specific Commit
|
||||
|
||||
```bash
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh -c abc123f
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh -c abc123f
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh -c abc123f
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh -c abc123f
|
||||
```
|
||||
|
||||
### Save Results to File
|
||||
|
||||
```bash
|
||||
# Save JSON output
|
||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted -o review-results.json
|
||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted -o security-results.json
|
||||
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted -o review-results.json
|
||||
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted -o security-results.json
|
||||
```
|
||||
|
||||
## Options
|
||||
@@ -113,12 +113,12 @@ Automated PR reviews in CI pipelines.
|
||||
|
||||
1. **Copy the pipeline template to your repo:**
|
||||
```bash
|
||||
cp ~/.config/mosaic/rails/codex/woodpecker/codex-review.yml your-repo/.woodpecker/
|
||||
cp ~/.config/mosaic/tools/codex/woodpecker/codex-review.yml your-repo/.woodpecker/
|
||||
```
|
||||
|
||||
2. **Copy the schemas directory:**
|
||||
```bash
|
||||
cp -r ~/.config/mosaic/rails/codex/schemas your-repo/.woodpecker/
|
||||
cp -r ~/.config/mosaic/tools/codex/schemas your-repo/.woodpecker/
|
||||
```
|
||||
|
||||
3. **Add Codex API key to Woodpecker:**
|
||||
@@ -203,7 +203,7 @@ Automated PR reviews in CI pipelines.
|
||||
|
||||
## Platform Support
|
||||
|
||||
Works with both **GitHub** and **Gitea** via the shared `~/.config/mosaic/rails/git/` infrastructure:
|
||||
Works with both **GitHub** and **Gitea** via the shared `~/.config/mosaic/tools/git/` infrastructure:
|
||||
- Auto-detects platform from git remote
|
||||
- Posts PR comments using `gh` (GitHub) or `tea` (Gitea)
|
||||
- Unified interface across both platforms
|
||||
@@ -261,5 +261,5 @@ For best results, use `gpt-5.2-codex` or newer for strongest review accuracy.
|
||||
## See Also
|
||||
|
||||
- `~/.config/mosaic/guides/CODE-REVIEW.md` — Manual code review checklist
|
||||
- `~/.config/mosaic/rails/git/` — Git helper scripts (issue/PR management)
|
||||
- `~/.config/mosaic/tools/git/` — Git helper scripts (issue/PR management)
|
||||
- OpenAI Codex CLI docs: https://developers.openai.com/codex/cli/
|
||||
65
tools/context/mosaic-context-loader.sh
Executable file
65
tools/context/mosaic-context-loader.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# mosaic-context-loader.sh — SessionStart hook for Claude Code
|
||||
# Injects mandatory Mosaic config files into agent context at session init.
|
||||
# Stdout from this script is added to Claude's context before processing.
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
|
||||
# Mandatory load order (per AGENTS.md contract)
|
||||
MANDATORY_FILES=(
|
||||
"$MOSAIC_HOME/SOUL.md"
|
||||
"$MOSAIC_HOME/USER.md"
|
||||
"$MOSAIC_HOME/STANDARDS.md"
|
||||
"$MOSAIC_HOME/AGENTS.md"
|
||||
"$MOSAIC_HOME/TOOLS.md"
|
||||
)
|
||||
|
||||
# E2E delivery guide (case-insensitive lookup)
|
||||
E2E_DELIVERY=""
|
||||
for candidate in \
|
||||
"$MOSAIC_HOME/guides/E2E-DELIVERY.md" \
|
||||
"$MOSAIC_HOME/guides/e2e-delivery.md"; do
|
||||
if [[ -f "$candidate" ]]; then
|
||||
E2E_DELIVERY="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Runtime-specific reference
|
||||
RUNTIME_FILE="$MOSAIC_HOME/runtime/claude/RUNTIME.md"
|
||||
|
||||
# Project-local AGENTS.md (cwd at session start)
|
||||
PROJECT_AGENTS=""
|
||||
if [[ -f "./AGENTS.md" ]]; then
|
||||
PROJECT_AGENTS="./AGENTS.md"
|
||||
fi
|
||||
|
||||
emit_file() {
|
||||
local filepath="$1"
|
||||
local label="${2:-$(basename "$filepath")}"
|
||||
if [[ -f "$filepath" ]]; then
|
||||
echo "=== MOSAIC: $label ==="
|
||||
cat "$filepath"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== MOSAIC CONTEXT INJECTION (SessionStart) ==="
|
||||
echo ""
|
||||
|
||||
for f in "${MANDATORY_FILES[@]}"; do
|
||||
emit_file "$f"
|
||||
done
|
||||
|
||||
if [[ -n "$E2E_DELIVERY" ]]; then
|
||||
emit_file "$E2E_DELIVERY" "E2E-DELIVERY.md"
|
||||
fi
|
||||
|
||||
if [[ -n "$PROJECT_AGENTS" ]]; then
|
||||
emit_file "$PROJECT_AGENTS" "Project AGENTS.md ($(pwd))"
|
||||
fi
|
||||
|
||||
emit_file "$RUNTIME_FILE" "Claude RUNTIME.md"
|
||||
|
||||
echo "=== END MOSAIC CONTEXT INJECTION ==="
|
||||
65
tools/coolify/README.md
Normal file
65
tools/coolify/README.md
Normal 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
61
tools/coolify/deploy.sh
Executable 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
65
tools/coolify/env-set.sh
Executable 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
52
tools/coolify/project-list.sh
Executable 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
53
tools/coolify/service-list.sh
Executable 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
62
tools/coolify/service-status.sh
Executable 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
52
tools/coolify/team-list.sh
Executable 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
|
||||
55
tools/glpi/README.md
Normal file
55
tools/glpi/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# GLPI Tool Suite
|
||||
|
||||
Manage GLPI IT service management (tickets, computers/assets, users).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `jq` and `curl` installed
|
||||
- GLPI credentials in `~/src/jarvis-brain/credentials.json` (or `$MOSAIC_CREDENTIALS_FILE`)
|
||||
- Required fields: `glpi.url`, `glpi.app_token`, `glpi.user_token`
|
||||
|
||||
## Authentication
|
||||
|
||||
GLPI uses a two-step auth flow:
|
||||
1. `session-init.sh` exchanges app_token + user_token for a session_token
|
||||
2. All subsequent calls use the session_token + app_token
|
||||
|
||||
The session token is cached at `~/.cache/mosaic/glpi-session` and auto-refreshed when expired.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `session-init.sh` | Initialize and cache API session |
|
||||
| `computer-list.sh` | List computers/IT assets |
|
||||
| `ticket-list.sh` | List tickets (filter by status) |
|
||||
| `ticket-create.sh` | Create a new ticket |
|
||||
| `user-list.sh` | List users |
|
||||
|
||||
## Common Options
|
||||
|
||||
- `-f json` — JSON output (default: table)
|
||||
- `-l limit` — Result count (default: 50)
|
||||
- `-h` — Show help
|
||||
|
||||
## API Reference
|
||||
|
||||
- Base URL: `https://help.uscllc.com/apirest.php`
|
||||
- Auth headers: `App-Token` + `Session-Token`
|
||||
- Pattern: RESTful item-based (`/ItemType/{id}`)
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# List all tickets
|
||||
~/.config/mosaic/tools/glpi/ticket-list.sh
|
||||
|
||||
# List only open tickets
|
||||
~/.config/mosaic/tools/glpi/ticket-list.sh -s new
|
||||
|
||||
# Create a ticket
|
||||
~/.config/mosaic/tools/glpi/ticket-create.sh -t "Server down" -c "Web server unresponsive" -p 4
|
||||
|
||||
# List computers as JSON
|
||||
~/.config/mosaic/tools/glpi/computer-list.sh -f json
|
||||
```
|
||||
59
tools/glpi/computer-list.sh
Executable file
59
tools/glpi/computer-list.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# computer-list.sh — List GLPI computers/assets
|
||||
#
|
||||
# Usage: computer-list.sh [-f format] [-l limit]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -l limit Number of results (default: 50)
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials glpi
|
||||
|
||||
FORMAT="table"
|
||||
LIMIT=50
|
||||
|
||||
while getopts "f:l:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
l) LIMIT="$OPTARG" ;;
|
||||
h) head -11 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-l limit]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SESSION_TOKEN=$("$SCRIPT_DIR/session-init.sh" -q)
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Session-Token: $SESSION_TOKEN" \
|
||||
"${GLPI_URL}/Computer?range=0-${LIMIT}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list computers (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "ID NAME SERIAL STATUS"
|
||||
echo "------ ---------------------------- ------------------ ----------"
|
||||
echo "$body" | jq -r '.[] | [
|
||||
(.id | tostring),
|
||||
.name,
|
||||
(.serial // "—"),
|
||||
(.states_id | tostring)
|
||||
] | @tsv' | while IFS=$'\t' read -r id name serial states_id; do
|
||||
printf "%-6s %-28s %-18s %s\n" "$id" "${name:0:28}" "${serial:0:18}" "$states_id"
|
||||
done
|
||||
85
tools/glpi/session-init.sh
Executable file
85
tools/glpi/session-init.sh
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# session-init.sh — Initialize GLPI API session
|
||||
#
|
||||
# Usage: session-init.sh [-f] [-q]
|
||||
#
|
||||
# Authenticates with GLPI and caches the session token at
|
||||
# ~/.cache/mosaic/glpi-session.
|
||||
#
|
||||
# Options:
|
||||
# -f Force re-authentication (ignore cached session)
|
||||
# -q Quiet mode — only output the session token
|
||||
# -h Show this help
|
||||
#
|
||||
# Environment variables (or credentials.json):
|
||||
# GLPI_URL — GLPI API base URL
|
||||
# GLPI_APP_TOKEN — GLPI application token
|
||||
# GLPI_USER_TOKEN — GLPI user token
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials glpi
|
||||
|
||||
CACHE_DIR="$HOME/.cache/mosaic"
|
||||
CACHE_FILE="$CACHE_DIR/glpi-session"
|
||||
FORCE=false
|
||||
QUIET=false
|
||||
|
||||
while getopts "fqh" opt; do
|
||||
case $opt in
|
||||
f) FORCE=true ;;
|
||||
q) QUIET=true ;;
|
||||
h) head -18 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f] [-q]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check cached session validity
|
||||
if [[ "$FORCE" == "false" ]] && [[ -f "$CACHE_FILE" ]]; then
|
||||
cached_token=$(cat "$CACHE_FILE")
|
||||
if [[ -n "$cached_token" ]]; then
|
||||
# Validate with a lightweight call
|
||||
http_code=$(curl -sk -o /dev/null -w "%{http_code}" \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Session-Token: $cached_token" \
|
||||
"${GLPI_URL}/getMyEntities")
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
[[ "$QUIET" == "false" ]] && echo "Using cached session (valid)" >&2
|
||||
echo "$cached_token"
|
||||
exit 0
|
||||
fi
|
||||
[[ "$QUIET" == "false" ]] && echo "Cached session expired, re-authenticating..." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Initialize session
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Authorization: user_token $GLPI_USER_TOKEN" \
|
||||
"${GLPI_URL}/initSession")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to initialize GLPI session (HTTP $http_code)" >&2
|
||||
echo "$body" | jq -r '.' 2>/dev/null >&2 || echo "$body" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
session_token=$(echo "$body" | jq -r '.session_token // empty')
|
||||
|
||||
if [[ -z "$session_token" ]]; then
|
||||
echo "Error: No session_token in response" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cache the session
|
||||
mkdir -p "$CACHE_DIR"
|
||||
echo "$session_token" > "$CACHE_FILE"
|
||||
chmod 600 "$CACHE_FILE"
|
||||
|
||||
[[ "$QUIET" == "false" ]] && echo "Session initialized and cached" >&2
|
||||
echo "$session_token"
|
||||
77
tools/glpi/ticket-create.sh
Executable file
77
tools/glpi/ticket-create.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# ticket-create.sh — Create a GLPI ticket
|
||||
#
|
||||
# Usage: ticket-create.sh -t <title> -c <content> [-p priority] [-y type]
|
||||
#
|
||||
# Options:
|
||||
# -t title Ticket title (required)
|
||||
# -c content Ticket description (required)
|
||||
# -p priority 1=VeryLow, 2=Low, 3=Medium (default), 4=High, 5=VeryHigh, 6=Major
|
||||
# -y type 1=Incident (default), 2=Request
|
||||
# -f format Output format: table (default), json
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials glpi
|
||||
|
||||
TITLE=""
|
||||
CONTENT=""
|
||||
PRIORITY=3
|
||||
TYPE=1
|
||||
FORMAT="table"
|
||||
|
||||
while getopts "t:c:p:y:f:h" opt; do
|
||||
case $opt in
|
||||
t) TITLE="$OPTARG" ;;
|
||||
c) CONTENT="$OPTARG" ;;
|
||||
p) PRIORITY="$OPTARG" ;;
|
||||
y) TYPE="$OPTARG" ;;
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
h) head -13 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 -t <title> -c <content> [-p priority] [-y type]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$TITLE" || -z "$CONTENT" ]]; then
|
||||
echo "Error: -t title and -c content are required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SESSION_TOKEN=$("$SCRIPT_DIR/session-init.sh" -q)
|
||||
|
||||
payload=$(jq -n \
|
||||
--arg name "$TITLE" \
|
||||
--arg content "$CONTENT" \
|
||||
--argjson priority "$PRIORITY" \
|
||||
--argjson type "$TYPE" \
|
||||
'{input: {name: $name, content: $content, priority: $priority, type: $type}}')
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" -X POST \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Session-Token: $SESSION_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload" \
|
||||
"${GLPI_URL}/Ticket")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "201" && "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to create ticket (HTTP $http_code)" >&2
|
||||
echo "$body" | jq -r '.' 2>/dev/null >&2 || echo "$body" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
else
|
||||
ticket_id=$(echo "$body" | jq -r '.id // .message // .')
|
||||
echo "Ticket created: #$ticket_id"
|
||||
echo " Title: $TITLE"
|
||||
echo " Priority: $PRIORITY"
|
||||
echo " Type: $([ "$TYPE" = "1" ] && echo "Incident" || echo "Request")"
|
||||
fi
|
||||
88
tools/glpi/ticket-list.sh
Executable file
88
tools/glpi/ticket-list.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# ticket-list.sh — List GLPI tickets
|
||||
#
|
||||
# Usage: ticket-list.sh [-f format] [-l limit] [-s status]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -l limit Number of results (default: 50)
|
||||
# -s status Filter: new, processing, pending, solved, closed
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials glpi
|
||||
|
||||
FORMAT="table"
|
||||
LIMIT=50
|
||||
STATUS=""
|
||||
|
||||
while getopts "f:l:s:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
l) LIMIT="$OPTARG" ;;
|
||||
s) STATUS="$OPTARG" ;;
|
||||
h) head -13 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-l limit] [-s status]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SESSION_TOKEN=$("$SCRIPT_DIR/session-init.sh" -q)
|
||||
|
||||
ENDPOINT="${GLPI_URL}/Ticket?range=0-${LIMIT}&order=DESC&sort=date_mod"
|
||||
|
||||
# Map status names to GLPI status IDs
|
||||
if [[ -n "$STATUS" ]]; then
|
||||
case "$STATUS" in
|
||||
new) STATUS_ID=1 ;;
|
||||
processing|assigned) STATUS_ID=2 ;;
|
||||
pending|planned) STATUS_ID=3 ;;
|
||||
solved) STATUS_ID=5 ;;
|
||||
closed) STATUS_ID=6 ;;
|
||||
*) echo "Error: Unknown status '$STATUS'. Use: new, processing, pending, solved, closed" >&2; exit 1 ;;
|
||||
esac
|
||||
ENDPOINT="${ENDPOINT}&searchText[status]=${STATUS_ID}"
|
||||
fi
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Session-Token: $SESSION_TOKEN" \
|
||||
"$ENDPOINT")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list tickets (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "ID PRIORITY STATUS TITLE DATE"
|
||||
echo "------ -------- ------ ---------------------------------------- ----------"
|
||||
echo "$body" | jq -r '.[] | [
|
||||
(.id | tostring),
|
||||
(.priority | tostring),
|
||||
(.status | tostring),
|
||||
.name,
|
||||
(.date_mod | split(" ")[0])
|
||||
] | @tsv' | while IFS=$'\t' read -r id priority status name date; do
|
||||
# Map priority numbers
|
||||
case "$priority" in
|
||||
1) pri="VLow" ;; 2) pri="Low" ;; 3) pri="Med" ;;
|
||||
4) pri="High" ;; 5) pri="VHigh" ;; 6) pri="Major" ;; *) pri="$priority" ;;
|
||||
esac
|
||||
# Map status numbers
|
||||
case "$status" in
|
||||
1) stat="New" ;; 2) stat="Proc" ;; 3) stat="Pend" ;;
|
||||
4) stat="Plan" ;; 5) stat="Solv" ;; 6) stat="Clos" ;; *) stat="$status" ;;
|
||||
esac
|
||||
printf "%-6s %-8s %-6s %-40s %s\n" "$id" "$pri" "$stat" "${name:0:40}" "$date"
|
||||
done
|
||||
61
tools/glpi/user-list.sh
Executable file
61
tools/glpi/user-list.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# user-list.sh — List GLPI users
|
||||
#
|
||||
# Usage: user-list.sh [-f format] [-l limit]
|
||||
#
|
||||
# Options:
|
||||
# -f format Output format: table (default), json
|
||||
# -l limit Number of results (default: 50)
|
||||
# -h Show this help
|
||||
set -euo pipefail
|
||||
|
||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$MOSAIC_HOME/tools/_lib/credentials.sh"
|
||||
load_credentials glpi
|
||||
|
||||
FORMAT="table"
|
||||
LIMIT=50
|
||||
|
||||
while getopts "f:l:h" opt; do
|
||||
case $opt in
|
||||
f) FORMAT="$OPTARG" ;;
|
||||
l) LIMIT="$OPTARG" ;;
|
||||
h) head -11 "$0" | grep "^#" | sed 's/^# \?//'; exit 0 ;;
|
||||
*) echo "Usage: $0 [-f format] [-l limit]" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SESSION_TOKEN=$("$SCRIPT_DIR/session-init.sh" -q)
|
||||
|
||||
response=$(curl -sk -w "\n%{http_code}" \
|
||||
-H "App-Token: $GLPI_APP_TOKEN" \
|
||||
-H "Session-Token: $SESSION_TOKEN" \
|
||||
"${GLPI_URL}/User?range=0-${LIMIT}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
echo "Error: Failed to list users (HTTP $http_code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo "$body" | jq '.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "ID USERNAME REALNAME FIRSTNAME ACTIVE"
|
||||
echo "------ -------------------- -------------------- -------------------- ------"
|
||||
echo "$body" | jq -r '.[] | [
|
||||
(.id | tostring),
|
||||
(.name // "—"),
|
||||
(.realname // "—"),
|
||||
(.firstname // "—"),
|
||||
(if .is_active == 1 then "yes" else "no" end)
|
||||
] | @tsv' | while IFS=$'\t' read -r id name realname firstname active; do
|
||||
printf "%-6s %-20s %-20s %-20s %s\n" \
|
||||
"$id" "${name:0:20}" "${realname:0:20}" "${firstname:0:20}" "$active"
|
||||
done
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user