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/
|
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.
|
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.
|
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.
|
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`.
|
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/rails/git/*.sh`).
|
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.
|
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?".
|
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.
|
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.
|
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`.
|
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.
|
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.
|
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.
|
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...`
|
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.
|
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.
|
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)
|
## Mode Declaration Protocol (Hard Rule)
|
||||||
|
|
||||||
|
|||||||
@@ -24,19 +24,19 @@ Scope:
|
|||||||
|
|
||||||
### MF-001 (QA rails path correction)
|
### MF-001 (QA rails path correction)
|
||||||
Updated:
|
Updated:
|
||||||
- `rails/qa/qa-hook-wrapper.sh`
|
- `tools/qa/qa-hook-wrapper.sh`
|
||||||
- `rails/qa/qa-hook-stdin.sh`
|
- `tools/qa/qa-hook-stdin.sh`
|
||||||
- `rails/qa/qa-hook-handler.sh`
|
- `tools/qa/qa-hook-handler.sh`
|
||||||
- `rails/qa/remediation-hook-handler.sh`
|
- `tools/qa/remediation-hook-handler.sh`
|
||||||
- `rails/qa/qa-queue-monitor.sh`
|
- `tools/qa/qa-queue-monitor.sh`
|
||||||
|
|
||||||
Change:
|
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)
|
### MF-002 + MF-003 (conditional loading/context detection)
|
||||||
Updated:
|
Updated:
|
||||||
- `rails/bootstrap/agent-lint.sh`
|
- `tools/bootstrap/agent-lint.sh`
|
||||||
- `rails/bootstrap/agent-upgrade.sh`
|
- `tools/bootstrap/agent-upgrade.sh`
|
||||||
- `templates/agent/SPEC.md`
|
- `templates/agent/SPEC.md`
|
||||||
|
|
||||||
Change:
|
Change:
|
||||||
@@ -58,7 +58,7 @@ Updated:
|
|||||||
- `skills/pr-reviewer/SKILL.md`
|
- `skills/pr-reviewer/SKILL.md`
|
||||||
|
|
||||||
Change:
|
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/...`.
|
- Replaced `~/.claude/skills/...` with `~/.config/mosaic/skills/...`.
|
||||||
|
|
||||||
### MF-006 (worktree skill docs hierarchy)
|
### MF-006 (worktree skill docs hierarchy)
|
||||||
@@ -109,7 +109,7 @@ These are required to support existing Claude runtime integration while keeping
|
|||||||
Executed checks:
|
Executed checks:
|
||||||
- `rg -n "~/.claude|\\.claude/|agent-guides" ~/src/agent-skills -S`
|
- `rg -n "~/.claude|\\.claude/|agent-guides" ~/src/agent-skills -S`
|
||||||
- Result: no matches after remediation.
|
- 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.
|
- Result: no invalid old-style QA rail paths remain.
|
||||||
- Installed runtime validation:
|
- 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.)
|
├── bin/ ← CLI tools (mosaic, mosaic-init, mosaic-doctor, etc.)
|
||||||
├── dist/ ← Bundled wizard (mosaic-wizard.mjs)
|
├── dist/ ← Bundled wizard (mosaic-wizard.mjs)
|
||||||
├── guides/ ← Operational guides
|
├── 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
|
├── runtime/ ← Runtime adapters + runtime-specific references
|
||||||
│ ├── claude/CLAUDE.md
|
│ ├── claude/CLAUDE.md
|
||||||
│ ├── claude/RUNTIME.md
|
│ ├── claude/RUNTIME.md
|
||||||
|
|||||||
12
STANDARDS.md
12
STANDARDS.md
@@ -12,16 +12,16 @@ Master/slave model:
|
|||||||
2. Load project-local `AGENTS.md` next.
|
2. Load project-local `AGENTS.md` next.
|
||||||
3. Respect repository-specific tooling and workflows.
|
3. Respect repository-specific tooling and workflows.
|
||||||
4. Use lifecycle scripts when available (`scripts/agent/*.sh`).
|
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
|
## Non-Negotiables
|
||||||
|
|
||||||
- Data files are authoritative; generated views are derived artifacts.
|
- Data files are authoritative; generated views are derived artifacts.
|
||||||
- Pull before edits when collaborating in shared repos.
|
- Pull before edits when collaborating in shared repos.
|
||||||
- Run validation checks before claiming completion.
|
- Run validation checks before claiming completion.
|
||||||
- Apply quality rails from `~/.config/mosaic/rails/` when relevant (review, QA, git workflow).
|
- Apply quality tools from `~/.config/mosaic/tools/` 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 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/rails/orchestrator-matrix/` with repo-local `.mosaic/orchestrator/` state.
|
- 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.
|
- Avoid hardcoded secrets and token leakage in remotes/commits.
|
||||||
- Do not perform destructive git/file actions without explicit instruction.
|
- 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.
|
- 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.
|
before task execution.
|
||||||
|
|
||||||
Runtime-compatible guides and rails are hosted at:
|
Runtime-compatible guides and tools are hosted at:
|
||||||
|
|
||||||
- `~/.config/mosaic/guides/`
|
- `~/.config/mosaic/guides/`
|
||||||
- `~/.config/mosaic/rails/`
|
- `~/.config/mosaic/tools/`
|
||||||
- `~/.config/mosaic/profiles/` (runtime-neutral domain/workflow/stack presets)
|
- `~/.config/mosaic/profiles/` (runtime-neutral domain/workflow/stack presets)
|
||||||
- `~/.config/mosaic/runtime/` (runtime-specific overlays)
|
- `~/.config/mosaic/runtime/` (runtime-specific overlays)
|
||||||
- `~/.config/mosaic/skills-local/` (local private skills shared across runtimes)
|
- `~/.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.
|
Centralized reference for tools, credentials, and CLI patterns available across all projects.
|
||||||
Project-specific tooling belongs in the project's `AGENTS.md`, not here.
|
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
|
```bash
|
||||||
# Issues
|
# Issues
|
||||||
~/.config/mosaic/rails/git/issue-create.sh
|
~/.config/mosaic/tools/git/issue-create.sh
|
||||||
~/.config/mosaic/rails/git/issue-close.sh
|
~/.config/mosaic/tools/git/issue-close.sh
|
||||||
|
|
||||||
# PRs
|
# PRs
|
||||||
~/.config/mosaic/rails/git/pr-create.sh
|
~/.config/mosaic/tools/git/pr-create.sh
|
||||||
~/.config/mosaic/rails/git/pr-merge.sh
|
~/.config/mosaic/tools/git/pr-merge.sh
|
||||||
|
|
||||||
# Milestones
|
# Milestones
|
||||||
~/.config/mosaic/rails/git/milestone-create.sh
|
~/.config/mosaic/tools/git/milestone-create.sh
|
||||||
|
|
||||||
# CI queue guard (required before push/merge)
|
# 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
|
```bash
|
||||||
# Code quality review
|
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted
|
||||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted
|
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted
|
||||||
|
```
|
||||||
|
|
||||||
# Security review
|
### Infrastructure — Portainer
|
||||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted
|
|
||||||
|
```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
|
## Git Providers
|
||||||
@@ -42,16 +112,13 @@ Mosaic wrappers at `~/.config/mosaic/rails/git/*.sh` handle platform detection a
|
|||||||
## Credentials
|
## Credentials
|
||||||
|
|
||||||
**Location:** (configure your credential file path)
|
**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.**
|
**Never expose actual values. Never commit credential files.**
|
||||||
|
|
||||||
## CLI Gotchas
|
## CLI Gotchas
|
||||||
|
|
||||||
(Add platform-specific CLI gotchas as you discover them. Examples: TTY requirements, default list limits, API fallback patterns.)
|
(Add platform-specific CLI gotchas as you discover them.)
|
||||||
|
|
||||||
## Custom Tools
|
|
||||||
|
|
||||||
(Add any machine-specific tools, scripts, or workflows here.)
|
|
||||||
|
|
||||||
## Safety Defaults
|
## Safety Defaults
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ Use wrapper commands from `~/.config/mosaic/bin/` for lifecycle rituals.
|
|||||||
## Migration Note
|
## Migration Note
|
||||||
|
|
||||||
Project-local `.claude/commands/*.md` should call `scripts/agent/*.sh` so behavior stays runtime-neutral.
|
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
|
bash scripts/agent/session-end.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Shared Rails
|
## Shared Tools
|
||||||
|
|
||||||
- Quality and orchestration guides: `~/.config/mosaic/guides/`
|
- Quality and orchestration guides: `~/.config/mosaic/guides/`
|
||||||
- Shared automation rails: `~/.config/mosaic/rails/`
|
- Shared automation tools: `~/.config/mosaic/tools/`
|
||||||
|
|
||||||
## Repo-Specific Notes
|
## Repo-Specific Notes
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ fi
|
|||||||
|
|
||||||
echo "[mosaic] Repo bootstrap complete: $TARGET_DIR"
|
echo "[mosaic] Repo bootstrap complete: $TARGET_DIR"
|
||||||
echo "[mosaic] Next: edit $TARGET_DIR/.mosaic/repo-hooks.sh with project workflows"
|
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 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"
|
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/^enabled:.*/enabled: true/" "$TARGET_DIR/.mosaic/quality-rails.yml"
|
||||||
sed -i "s/^template:.*/template: \"$QUALITY_TEMPLATE\"/" "$TARGET_DIR/.mosaic/quality-rails.yml"
|
sed -i "s/^template:.*/template: \"$QUALITY_TEMPLATE\"/" "$TARGET_DIR/.mosaic/quality-rails.yml"
|
||||||
fi
|
fi
|
||||||
echo "[mosaic] Applied quality rails template: $QUALITY_TEMPLATE"
|
echo "[mosaic] Applied quality tools template: $QUALITY_TEMPLATE"
|
||||||
else
|
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
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -149,9 +149,9 @@ expect_file "$MOSAIC_HOME/STANDARDS.md"
|
|||||||
expect_file "$MOSAIC_HOME/USER.md"
|
expect_file "$MOSAIC_HOME/USER.md"
|
||||||
expect_file "$MOSAIC_HOME/TOOLS.md"
|
expect_file "$MOSAIC_HOME/TOOLS.md"
|
||||||
expect_dir "$MOSAIC_HOME/guides"
|
expect_dir "$MOSAIC_HOME/guides"
|
||||||
expect_dir "$MOSAIC_HOME/rails"
|
expect_dir "$MOSAIC_HOME/tools"
|
||||||
expect_dir "$MOSAIC_HOME/rails/quality"
|
expect_dir "$MOSAIC_HOME/tools/quality"
|
||||||
expect_dir "$MOSAIC_HOME/rails/orchestrator-matrix"
|
expect_dir "$MOSAIC_HOME/tools/orchestrator-matrix"
|
||||||
expect_dir "$MOSAIC_HOME/profiles"
|
expect_dir "$MOSAIC_HOME/profiles"
|
||||||
expect_dir "$MOSAIC_HOME/templates/agent"
|
expect_dir "$MOSAIC_HOME/templates/agent"
|
||||||
expect_dir "$MOSAIC_HOME/skills"
|
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-publish"
|
||||||
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-consume"
|
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-consume"
|
||||||
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-cycle"
|
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-cycle"
|
||||||
expect_file "$MOSAIC_HOME/rails/git/ci-queue-wait.sh"
|
expect_file "$MOSAIC_HOME/tools/git/ci-queue-wait.sh"
|
||||||
expect_file "$MOSAIC_HOME/rails/git/pr-ci-wait.sh"
|
expect_file "$MOSAIC_HOME/tools/git/pr-ci-wait.sh"
|
||||||
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/transport/matrix_transport.py"
|
expect_file "$MOSAIC_HOME/tools/orchestrator-matrix/transport/matrix_transport.py"
|
||||||
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/controller/tasks_md_sync.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/mcp/SEQUENTIAL-THINKING.json"
|
||||||
expect_file "$MOSAIC_HOME/runtime/claude/RUNTIME.md"
|
expect_file "$MOSAIC_HOME/runtime/claude/RUNTIME.md"
|
||||||
expect_file "$MOSAIC_HOME/runtime/codex/RUNTIME.md"
|
expect_file "$MOSAIC_HOME/runtime/codex/RUNTIME.md"
|
||||||
|
|||||||
@@ -138,9 +138,9 @@ Write-Host "[mosaic-doctor] Mosaic home: $MosaicHome"
|
|||||||
# Canonical Mosaic checks
|
# Canonical Mosaic checks
|
||||||
Expect-File (Join-Path $MosaicHome "STANDARDS.md")
|
Expect-File (Join-Path $MosaicHome "STANDARDS.md")
|
||||||
Expect-Dir (Join-Path $MosaicHome "guides")
|
Expect-Dir (Join-Path $MosaicHome "guides")
|
||||||
Expect-Dir (Join-Path $MosaicHome "rails")
|
Expect-Dir (Join-Path $MosaicHome "tools")
|
||||||
Expect-Dir (Join-Path $MosaicHome "rails\quality")
|
Expect-Dir (Join-Path $MosaicHome "tools\quality")
|
||||||
Expect-Dir (Join-Path $MosaicHome "rails\orchestrator-matrix")
|
Expect-Dir (Join-Path $MosaicHome "tools\orchestrator-matrix")
|
||||||
Expect-Dir (Join-Path $MosaicHome "profiles")
|
Expect-Dir (Join-Path $MosaicHome "profiles")
|
||||||
Expect-Dir (Join-Path $MosaicHome "templates\agent")
|
Expect-Dir (Join-Path $MosaicHome "templates\agent")
|
||||||
Expect-Dir (Join-Path $MosaicHome "skills")
|
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-publish")
|
||||||
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-consume")
|
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 "bin\mosaic-orchestrator-matrix-cycle")
|
||||||
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.ps1")
|
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.ps1")
|
||||||
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.sh")
|
Expect-File (Join-Path $MosaicHome "tools\git\ci-queue-wait.sh")
|
||||||
Expect-File (Join-Path $MosaicHome "rails\git\pr-ci-wait.sh")
|
Expect-File (Join-Path $MosaicHome "tools\git\pr-ci-wait.sh")
|
||||||
Expect-File (Join-Path $MosaicHome "rails\orchestrator-matrix\transport\matrix_transport.py")
|
Expect-File (Join-Path $MosaicHome "tools\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\orchestrator-matrix\controller\tasks_md_sync.py")
|
||||||
Expect-File (Join-Path $MosaicHome "runtime\mcp\SEQUENTIAL-THINKING.json")
|
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\claude\RUNTIME.md")
|
||||||
Expect-File (Join-Path $MosaicHome "runtime\codex\RUNTIME.md")
|
Expect-File (Join-Path $MosaicHome "runtime\codex\RUNTIME.md")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
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
|
if [[ ! -f "$BRIDGE" ]]; then
|
||||||
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
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
|
if [[ ! -f "$BRIDGE" ]]; then
|
||||||
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
echo "[mosaic-orch-matrix] missing transport bridge: $BRIDGE" >&2
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
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
|
if [[ ! -f "$CTRL" ]]; then
|
||||||
echo "[mosaic-orchestrator] missing controller: $CTRL" >&2
|
echo "[mosaic-orchestrator] missing controller: $CTRL" >&2
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
|
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
|
if [[ ! -f "$SYNC" ]]; then
|
||||||
echo "[mosaic-orchestrator-sync] missing sync script: $SYNC" >&2
|
echo "[mosaic-orchestrator-sync] missing sync script: $SYNC" >&2
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ usage() {
|
|||||||
cat <<USAGE
|
cat <<USAGE
|
||||||
Usage: $(basename "$0") --template <name> [--target <dir>]
|
Usage: $(basename "$0") --template <name> [--target <dir>]
|
||||||
|
|
||||||
Apply Mosaic quality rails templates into a project.
|
Apply Mosaic quality tools templates into a project.
|
||||||
|
|
||||||
Templates:
|
Templates:
|
||||||
typescript-node
|
typescript-node
|
||||||
@@ -55,7 +55,7 @@ if [[ ! -d "$TARGET_DIR" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SCRIPT="$MOSAIC_HOME/rails/quality/scripts/install.sh"
|
SCRIPT="$MOSAIC_HOME/tools/quality/scripts/install.sh"
|
||||||
if [[ ! -x "$SCRIPT" ]]; then
|
if [[ ! -x "$SCRIPT" ]]; then
|
||||||
echo "[mosaic-quality] Missing install script: $SCRIPT" >&2
|
echo "[mosaic-quality] Missing install script: $SCRIPT" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ if [[ ! -d "$TARGET_DIR" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SCRIPT="$MOSAIC_HOME/rails/quality/scripts/verify.sh"
|
SCRIPT="$MOSAIC_HOME/tools/quality/scripts/verify.sh"
|
||||||
if [[ ! -x "$SCRIPT" ]]; then
|
if [[ ! -x "$SCRIPT" ]]; then
|
||||||
echo "[mosaic-quality] Missing verify script: $SCRIPT" >&2
|
echo "[mosaic-quality] Missing verify script: $SCRIPT" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Authentication & Authorization Guide
|
# Authentication & Authorization Guide
|
||||||
|
|
||||||
## Before Starting
|
## 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
|
2. Review existing auth implementation in codebase
|
||||||
3. Review Vault secrets structure: `docs/vault-secrets-structure.md`
|
3. Review Vault secrets structure: `docs/vault-secrets-structure.md`
|
||||||
|
|
||||||
@@ -115,6 +115,41 @@ class TestAuthentication:
|
|||||||
pass
|
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
|
## Common Vulnerabilities to Avoid
|
||||||
|
|
||||||
1. **Broken Authentication**
|
1. **Broken Authentication**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Backend Development Guide
|
# Backend Development Guide
|
||||||
|
|
||||||
## Before Starting
|
## 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`
|
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||||
3. Review API contracts and database schema
|
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
|
```bash
|
||||||
# Automated bootstrap (recommended)
|
# Automated bootstrap (recommended)
|
||||||
~/.config/mosaic/rails/bootstrap/init-project.sh \
|
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||||
--name "my-project" \
|
--name "my-project" \
|
||||||
--type "nestjs-nextjs" \
|
--type "nestjs-nextjs" \
|
||||||
--repo "https://git.mosaicstack.dev/owner/repo"
|
--repo "https://git.mosaicstack.dev/owner/repo"
|
||||||
@@ -240,10 +240,10 @@ Documentation root hygiene (HARD RULE):
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Use the init script
|
# 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
|
# 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
|
### 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.
|
Reserve `0.1.0` for the MVP release milestone.
|
||||||
|
|
||||||
```bash
|
```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:
|
# 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
|
```bash
|
||||||
# Copy Codex review pipeline
|
# Copy Codex review pipeline
|
||||||
mkdir -p .woodpecker/schemas
|
mkdir -p .woodpecker/schemas
|
||||||
cp ~/.config/mosaic/rails/codex/woodpecker/codex-review.yml .woodpecker/
|
cp ~/.config/mosaic/tools/codex/woodpecker/codex-review.yml .woodpecker/
|
||||||
cp ~/.config/mosaic/rails/codex/schemas/*.json .woodpecker/schemas/
|
cp ~/.config/mosaic/tools/codex/schemas/*.json .woodpecker/schemas/
|
||||||
|
|
||||||
# Add codex_api_key secret to Woodpecker CI dashboard
|
# Add codex_api_key secret to Woodpecker CI dashboard
|
||||||
```
|
```
|
||||||
@@ -366,7 +366,7 @@ fi
|
|||||||
# (execute the command block under "Quality Gates")
|
# (execute the command block under "Quality Gates")
|
||||||
|
|
||||||
# Test Codex review (if configured)
|
# 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
|
# Verify sequential-thinking MCP remains configured
|
||||||
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking --check
|
~/.config/mosaic/bin/mosaic-ensure-sequential-thinking --check
|
||||||
@@ -434,7 +434,7 @@ fi
|
|||||||
Full project bootstrap with interactive and flag-based modes:
|
Full project bootstrap with interactive and flag-based modes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/.config/mosaic/rails/bootstrap/init-project.sh \
|
~/.config/mosaic/tools/bootstrap/init-project.sh \
|
||||||
--name "My Project" \
|
--name "My Project" \
|
||||||
--type "nestjs-nextjs" \
|
--type "nestjs-nextjs" \
|
||||||
--repo "https://git.mosaicstack.dev/owner/repo" \
|
--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:
|
Initialize standard labels and the first pre-MVP milestone:
|
||||||
|
|
||||||
```bash
|
```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)
|
- [ ] `.env.example` exists (if project uses env vars)
|
||||||
- [ ] CI/CD pipeline configured (if using Woodpecker/GitHub Actions)
|
- [ ] CI/CD pipeline configured (if using Woodpecker/GitHub Actions)
|
||||||
- [ ] Python publish path configured in CI (if project ships Python packages)
|
- [ ] 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`.
|
- PR target for delivery is `main`.
|
||||||
- Direct pushes to `main` are prohibited.
|
- Direct pushes to `main` are prohibited.
|
||||||
- Merge to `main` MUST be squash-only.
|
- 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
|
## Review Checklist
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ Use `~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md` whenever code/A
|
|||||||
### Getting Context
|
### Getting Context
|
||||||
```bash
|
```bash
|
||||||
# List the issue being addressed
|
# 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
|
# View the changes
|
||||||
git diff main...HEAD
|
git diff main...HEAD
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Frontend Development Guide
|
# Frontend Development Guide
|
||||||
|
|
||||||
## Before Starting
|
## 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`
|
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||||
3. Review existing components and patterns in the codebase
|
3. Review existing components and patterns in the codebase
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Infrastructure & DevOps Guide
|
# Infrastructure & DevOps Guide
|
||||||
|
|
||||||
## Before Starting
|
## 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`
|
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||||
3. Review existing infrastructure configuration
|
3. Review existing infrastructure configuration
|
||||||
|
|
||||||
@@ -97,10 +97,10 @@ readinessProbe:
|
|||||||
periodSeconds: 3
|
periodSeconds: 3
|
||||||
```
|
```
|
||||||
|
|
||||||
## CI/CD Pipelines
|
## CI/CD Pipelines
|
||||||
|
|
||||||
### Pipeline Stages
|
### Pipeline Stages
|
||||||
1. **Lint**: Code style and static analysis
|
1. **Lint**: Code style and static analysis
|
||||||
2. **Test**: Unit and integration tests
|
2. **Test**: Unit and integration tests
|
||||||
3. **Build**: Compile and package
|
3. **Build**: Compile and package
|
||||||
4. **Scan**: Security and vulnerability scanning
|
4. **Scan**: Security and vulnerability scanning
|
||||||
@@ -109,65 +109,96 @@ readinessProbe:
|
|||||||
### Pipeline Security
|
### Pipeline Security
|
||||||
- Use secrets management (not hardcoded)
|
- Use secrets management (not hardcoded)
|
||||||
- Pin action/image versions
|
- Pin action/image versions
|
||||||
- Implement approval gates for production
|
- Implement approval gates for production
|
||||||
- Audit pipeline access
|
- Audit pipeline access
|
||||||
|
|
||||||
## Steered-Autonomous Deployment (Hard Rule)
|
## Steered-Autonomous Deployment (Hard Rule)
|
||||||
|
|
||||||
In lights-out mode, the agent owns deployment end-to-end when deployment is in scope.
|
In lights-out mode, the agent owns deployment end-to-end when deployment is in scope.
|
||||||
The human is escalation-only for missing access, hard policy conflicts, or irreversible risk.
|
The human is escalation-only for missing access, hard policy conflicts, or irreversible risk.
|
||||||
|
|
||||||
### Deployment Target Selection
|
### Deployment Target Selection
|
||||||
|
|
||||||
1. Use explicit target from `docs/PRD.md` / `docs/PRD.json` or `docs/DEPLOYMENT.md`.
|
1. Use explicit target from `docs/PRD.md` / `docs/PRD.json` or `docs/DEPLOYMENT.md`.
|
||||||
2. If unspecified, infer from existing project config/integration.
|
2. If unspecified, infer from existing project config/integration.
|
||||||
3. If multiple targets exist, choose the target already wired in CI/CD and document rationale.
|
3. If multiple targets exist, choose the target already wired in CI/CD and document rationale.
|
||||||
|
|
||||||
### Supported Targets
|
### Supported Targets
|
||||||
|
|
||||||
- **Portainer**: Deploy via configured stack webhook/API, then verify service health and container status.
|
- **Portainer**: Deploy via `~/.config/mosaic/tools/portainer/stack-redeploy.sh`, then verify with `stack-status.sh`.
|
||||||
- **Coolify**: Trigger deployment via Coolify API/webhook, then verify deployment status and endpoint health.
|
- **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.
|
- **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.
|
- **Other SaaS providers**: Use provider CLI/API/runbook with the same validation and rollback gates.
|
||||||
|
|
||||||
### Image Tagging and Promotion (Hard Rule)
|
### Coolify API Operations
|
||||||
|
|
||||||
For containerized deployments:
|
```bash
|
||||||
|
# List projects and services
|
||||||
1. Build immutable image tags: `sha-<shortsha>` and `v{base-version}-rc.{build}`.
|
~/.config/mosaic/tools/coolify/project-list.sh
|
||||||
2. Use mutable environment tags only as pointers: `testing`, optional `staging`, and `prod`.
|
~/.config/mosaic/tools/coolify/service-list.sh
|
||||||
3. Deploy by immutable digest, not by mutable tag alone.
|
|
||||||
4. Promote the exact tested digest between environments (no rebuild between testing and prod).
|
# Check service status
|
||||||
5. Do not use `latest` or `dev` as deployment references.
|
~/.config/mosaic/tools/coolify/service-status.sh -u <uuid>
|
||||||
|
|
||||||
Blue-green is the default strategy for production promotion.
|
# Set env vars (takes effect on next deploy)
|
||||||
Canary is allowed only when automated SLO/error-rate gates and auto-rollback triggers are implemented.
|
~/.config/mosaic/tools/coolify/env-set.sh -u <uuid> -k KEY -v VALUE
|
||||||
|
|
||||||
### Post-Deploy Validation (REQUIRED)
|
# Deploy
|
||||||
|
~/.config/mosaic/tools/coolify/deploy.sh -u <uuid>
|
||||||
1. Health endpoints return expected status.
|
```
|
||||||
2. Critical smoke tests pass in target environment.
|
|
||||||
3. Running version and digest match the promoted release candidate.
|
**Known Coolify Limitations:**
|
||||||
4. Observability signals (errors/latency) are within expected thresholds.
|
- FQDN updates on compose sub-apps not supported via API (DB workaround required)
|
||||||
|
- Compose files must be base64-encoded in `docker_compose_raw` field
|
||||||
### Rollback Rule
|
- Magic variables (`SERVICE_FQDN_*`) require list-style env syntax, not dict-style
|
||||||
|
- Rate limit: 200 requests per interval
|
||||||
If post-deploy validation fails:
|
|
||||||
|
### Stack Health Check
|
||||||
1. Execute rollback/redeploy-safe path immediately.
|
|
||||||
2. Mark deployment as blocked in `docs/TASKS.md`.
|
Verify all infrastructure services are reachable:
|
||||||
3. Record failure evidence and next remediation step in scratchpad and release notes.
|
|
||||||
|
```bash
|
||||||
### Registry Retention and Cleanup
|
~/.config/mosaic/tools/health/stack-health.sh
|
||||||
|
```
|
||||||
Cleanup MUST be automated.
|
|
||||||
|
### Image Tagging and Promotion (Hard Rule)
|
||||||
- Keep all final release tags (`vX.Y.Z`) indefinitely.
|
|
||||||
- Keep active environment digests (`prod`, `testing`, and active blue/green slots).
|
For containerized deployments:
|
||||||
- Keep recent RC tags (`vX.Y.Z-rc.N`) based on retention window.
|
|
||||||
- Remove stale `sha-*` and RC tags outside retention window if they are not actively deployed.
|
1. Build immutable image tags: `sha-<shortsha>` and `v{base-version}-rc.{build}`.
|
||||||
|
2. Use mutable environment tags only as pointers: `testing`, optional `staging`, and `prod`.
|
||||||
## Monitoring & Logging
|
3. Deploy by immutable digest, not by mutable tag alone.
|
||||||
|
4. Promote the exact tested digest between environments (no rebuild between testing and prod).
|
||||||
|
5. Do not use `latest` or `dev` as deployment references.
|
||||||
|
|
||||||
|
Blue-green is the default strategy for production promotion.
|
||||||
|
Canary is allowed only when automated SLO/error-rate gates and auto-rollback triggers are implemented.
|
||||||
|
|
||||||
|
### Post-Deploy Validation (REQUIRED)
|
||||||
|
|
||||||
|
1. Health endpoints return expected status.
|
||||||
|
2. Critical smoke tests pass in target environment.
|
||||||
|
3. Running version and digest match the promoted release candidate.
|
||||||
|
4. Observability signals (errors/latency) are within expected thresholds.
|
||||||
|
|
||||||
|
### Rollback Rule
|
||||||
|
|
||||||
|
If post-deploy validation fails:
|
||||||
|
|
||||||
|
1. Execute rollback/redeploy-safe path immediately.
|
||||||
|
2. Mark deployment as blocked in `docs/TASKS.md`.
|
||||||
|
3. Record failure evidence and next remediation step in scratchpad and release notes.
|
||||||
|
|
||||||
|
### Registry Retention and Cleanup
|
||||||
|
|
||||||
|
Cleanup MUST be automated.
|
||||||
|
|
||||||
|
- Keep all final release tags (`vX.Y.Z`) indefinitely.
|
||||||
|
- Keep active environment digests (`prod`, `testing`, and active blue/green slots).
|
||||||
|
- Keep recent RC tags (`vX.Y.Z-rc.N`) based on retention window.
|
||||||
|
- Remove stale `sha-*` and RC tags outside retention window if they are not actively deployed.
|
||||||
|
|
||||||
|
## Monitoring & Logging
|
||||||
|
|
||||||
### Logging Standards
|
### Logging Standards
|
||||||
- Use structured logging (JSON)
|
- Use structured logging (JSON)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Before Starting
|
## 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`
|
2. Create scratchpad: `docs/scratchpads/{issue-number}-{short-name}.md`
|
||||||
3. Review `docs/PRD.md` or `docs/PRD.json` as the requirements source.
|
3. Review `docs/PRD.md` or `docs/PRD.json` as the requirements source.
|
||||||
4. Review acceptance criteria and affected change surfaces.
|
4. Review acceptance criteria and affected change surfaces.
|
||||||
|
|||||||
@@ -870,7 +870,7 @@ Required sequence:
|
|||||||
1. Merge PR to `main` (squash) via Mosaic wrapper.
|
1. Merge PR to `main` (squash) via Mosaic wrapper.
|
||||||
2. Monitor CI to terminal status:
|
2. Monitor CI to terminal status:
|
||||||
```bash
|
```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.
|
3. Require green status before claiming completion.
|
||||||
4. If CI fails, create remediation task(s) and continue until green.
|
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:
|
Before pushing a branch or merging a PR, guard against overlapping project pipelines:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose push -B main
|
~/.config/mosaic/tools/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 merge -B main
|
||||||
```
|
```
|
||||||
|
|
||||||
Behavior:
|
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.
|
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`.
|
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`.
|
3. Detect provider first via `~/.config/mosaic/tools/git/detect-platform.sh`.
|
||||||
4. For issue/PR/milestone operations, use Mosaic wrappers first (`~/.config/mosaic/rails/git/*.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.
|
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`).
|
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`).
|
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.
|
5. `remediate` - fix all findings and any test failures.
|
||||||
6. `review` - re-review remediated changes until blockers are cleared.
|
6. `review` - re-review remediated changes until blockers are cleared.
|
||||||
7. `commit` - commit only when the logical unit passes tests and review.
|
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.
|
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.
|
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`.
|
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/rails/git/pr-ci-wait.sh` for PR-based workflow).
|
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).
|
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).
|
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.
|
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)
|
### Post-PR Hard Gate (Execute Sequentially, No Exceptions)
|
||||||
|
|
||||||
1. `~/.config/mosaic/rails/git/ci-queue-wait.sh --purpose merge -B main`
|
1. `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose merge -B main`
|
||||||
2. `~/.config/mosaic/rails/git/pr-merge.sh -n <PR_NUMBER> -m squash`
|
2. `~/.config/mosaic/tools/git/pr-merge.sh -n <PR_NUMBER> -m squash`
|
||||||
3. `~/.config/mosaic/rails/git/pr-ci-wait.sh -n <PR_NUMBER>`
|
3. `~/.config/mosaic/tools/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)
|
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.
|
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.
|
6. Do not ask the human to perform routine merge/close operations.
|
||||||
7. Do not claim completion before step 4 succeeds.
|
7. Do not claim completion before step 4 succeeds.
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ Provider options:
|
|||||||
1. Gitea (preferred when available) via Mosaic helper:
|
1. Gitea (preferred when available) via Mosaic helper:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/.config/mosaic/rails/git/issue-create.sh \
|
~/.config/mosaic/tools/git/issue-create.sh \
|
||||||
-t "Phase 1: Critical Security Fixes" \
|
-t "Phase 1: Critical Security Fixes" \
|
||||||
-b "$(cat <<'EOF'
|
-b "$(cat <<'EOF'
|
||||||
## Findings
|
## Findings
|
||||||
@@ -412,15 +412,15 @@ git push
|
|||||||
and checklist completed (`~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md`) when applicable.
|
and checklist completed (`~/.config/mosaic/templates/docs/DOCUMENTATION-CHECKLIST.md`) when applicable.
|
||||||
13. **PR + CI + Issue Closure Gate** (HARD RULE for source-code tasks):
|
13. **PR + CI + Issue Closure Gate** (HARD RULE for source-code tasks):
|
||||||
- Before merging, run queue guard:
|
- 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):
|
- 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:
|
- 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:
|
- 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:
|
- 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.
|
- 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 stop at "PR created" or "PR merged pending CI".
|
||||||
- Do NOT claim completion before CI is green and issue/internal ref is closed.
|
- 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}
|
cd {project_path}
|
||||||
|
|
||||||
# Code quality review
|
# 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
|
# 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
|
### 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`.
|
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.
|
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"`
|
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}`
|
11. Push: `git push origin {branch}`
|
||||||
12. Report result as JSON (see format below)
|
12. Report result as JSON (see format below)
|
||||||
|
|
||||||
## Git Scripts
|
## Git Scripts
|
||||||
|
|
||||||
For issue/PR/milestone operations, use scripts (NOT raw tea/gh):
|
For issue/PR/milestone operations, use scripts (NOT raw tea/gh):
|
||||||
- `~/.config/mosaic/rails/git/issue-view.sh -i {N}`
|
- `~/.config/mosaic/tools/git/issue-view.sh -i {N}`
|
||||||
- `~/.config/mosaic/rails/git/pr-create.sh -t "Title" -b "Desc" -B main`
|
- `~/.config/mosaic/tools/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/tools/git/ci-queue-wait.sh --purpose push|merge -B main`
|
||||||
- `~/.config/mosaic/rails/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
- `~/.config/mosaic/tools/git/pr-merge.sh -n {PR_NUMBER} -m squash`
|
||||||
- `~/.config/mosaic/rails/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
- `~/.config/mosaic/tools/git/pr-ci-wait.sh -n {PR_NUMBER}`
|
||||||
- `~/.config/mosaic/rails/git/issue-close.sh -i {N}`
|
- `~/.config/mosaic/tools/git/issue-close.sh -i {N}`
|
||||||
|
|
||||||
Standard git commands (pull, commit, push, checkout) are fine.
|
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**:
|
5. **Close milestone in provider**:
|
||||||
- Gitea/GitHub:
|
- Gitea/GitHub:
|
||||||
```bash
|
```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).
|
- GitLab: close milestone via provider workflow (CLI or web UI).
|
||||||
If provider tooling is unavailable, record milestone closure status in `docs/TASKS.md` notes.
|
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"/bin/*
|
||||||
chmod +x "$TARGET_DIR"/install.sh
|
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"
|
ok "Framework installed to $TARGET_DIR"
|
||||||
|
|
||||||
step "Post-install tasks"
|
step "Post-install tasks"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This file applies only to Claude runtime behavior.
|
|||||||
3. Treat sequential-thinking MCP as required.
|
3. Treat sequential-thinking MCP as required.
|
||||||
4. If runtime config conflicts with global rules, global rules win.
|
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`.
|
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.
|
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...`
|
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.
|
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": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "~/.config/mosaic/rails/qa/qa-hook-stdin.sh",
|
"command": "~/.config/mosaic/tools/qa/qa-hook-stdin.sh",
|
||||||
"timeout": 60
|
"timeout": 60
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ This file applies only to Codex runtime behavior.
|
|||||||
3. Treat sequential-thinking MCP as required.
|
3. Treat sequential-thinking MCP as required.
|
||||||
4. If runtime config conflicts with global rules, global rules win.
|
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`.
|
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.
|
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...`
|
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.
|
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.
|
3. Treat sequential-thinking MCP as required.
|
||||||
4. If runtime config conflicts with global rules, global rules win.
|
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`.
|
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.
|
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...`
|
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.
|
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
|
## 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.
|
- Do not edit generated project views directly when the repo defines canonical data sources.
|
||||||
- Pull/rebase before edits in shared repositories.
|
- Pull/rebase before edits in shared repositories.
|
||||||
- Run project verification commands before claiming completion.
|
- 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:
|
If the project's `.woodpecker.yml` doesn't already have a `kaniko_setup` anchor in its `variables:` section, add it:
|
||||||
|
|
||||||
```bash
|
```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:
|
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:
|
Use the generator script with the user's answers:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/.config/mosaic/rails/cicd/generate-docker-steps.sh \
|
~/.config/mosaic/tools/cicd/generate-docker-steps.sh \
|
||||||
--registry REGISTRY \
|
--registry REGISTRY \
|
||||||
--org ORG \
|
--org ORG \
|
||||||
--repo REPO \
|
--repo REPO \
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ Run independent reviews:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Code quality review (Codex)
|
# 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)
|
# 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.
|
**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 $FIX_HINT && ! $JSON_OUTPUT; then
|
||||||
if [[ "$has_runtime" == "MISS" || "$has_agents" == "MISS" ]]; 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
|
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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ set -e
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
TEMPLATE_DIR="$HOME/.config/mosaic/templates/agent"
|
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"
|
SEQUENTIAL_MCP_SCRIPT="$HOME/.config/mosaic/bin/mosaic-ensure-sequential-thinking"
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
@@ -403,7 +403,7 @@ echo "Created docs/scratchpads/, docs/reports/*, docs/tasks/, docs/releases/, do
|
|||||||
|
|
||||||
# Set up CI/CD pipeline
|
# Set up CI/CD pipeline
|
||||||
if [[ "$SKIP_CI" != true ]]; then
|
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
|
if [[ -d "$CODEX_DIR/woodpecker" ]]; then
|
||||||
mkdir -p .woodpecker/schemas
|
mkdir -p .woodpecker/schemas
|
||||||
cp "$CODEX_DIR/woodpecker/codex-review.yml" .woodpecker/
|
cp "$CODEX_DIR/woodpecker/codex-review.yml" .woodpecker/
|
||||||
@@ -416,7 +416,7 @@ fi
|
|||||||
|
|
||||||
# Generate Docker build/push/link pipeline steps
|
# Generate Docker build/push/link pipeline steps
|
||||||
if [[ "$CICD_DOCKER" == true ]]; then
|
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
|
if [[ -x "$CICD_SCRIPT" ]]; then
|
||||||
# Parse org and repo from git remote
|
# Parse org and repo from git remote
|
||||||
CICD_REGISTRY=""
|
CICD_REGISTRY=""
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
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"
|
source "$GIT_SCRIPT_DIR/detect-platform.sh"
|
||||||
|
|
||||||
SKIP_MILESTONE=false
|
SKIP_MILESTONE=false
|
||||||
@@ -50,45 +50,45 @@ Security vulnerability review focusing on:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Code review
|
# Code review
|
||||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted
|
~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted
|
||||||
|
|
||||||
# Security review
|
# Security review
|
||||||
~/.config/mosaic/rails/codex/codex-security-review.sh --uncommitted
|
~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted
|
||||||
```
|
```
|
||||||
|
|
||||||
### Review a Pull Request
|
### Review a Pull Request
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Review and post findings as a PR comment
|
# 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
|
# 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
|
### Review Against Base Branch
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Code review changes vs main
|
# 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
|
# 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
|
### Review a Specific Commit
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
~/.config/mosaic/rails/codex/codex-code-review.sh -c abc123f
|
~/.config/mosaic/tools/codex/codex-code-review.sh -c abc123f
|
||||||
~/.config/mosaic/rails/codex/codex-security-review.sh -c abc123f
|
~/.config/mosaic/tools/codex/codex-security-review.sh -c abc123f
|
||||||
```
|
```
|
||||||
|
|
||||||
### Save Results to File
|
### Save Results to File
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Save JSON output
|
# Save JSON output
|
||||||
~/.config/mosaic/rails/codex/codex-code-review.sh --uncommitted -o review-results.json
|
~/.config/mosaic/tools/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-security-review.sh --uncommitted -o security-results.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
@@ -113,12 +113,12 @@ Automated PR reviews in CI pipelines.
|
|||||||
|
|
||||||
1. **Copy the pipeline template to your repo:**
|
1. **Copy the pipeline template to your repo:**
|
||||||
```bash
|
```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:**
|
2. **Copy the schemas directory:**
|
||||||
```bash
|
```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:**
|
3. **Add Codex API key to Woodpecker:**
|
||||||
@@ -203,7 +203,7 @@ Automated PR reviews in CI pipelines.
|
|||||||
|
|
||||||
## Platform Support
|
## 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
|
- Auto-detects platform from git remote
|
||||||
- Posts PR comments using `gh` (GitHub) or `tea` (Gitea)
|
- Posts PR comments using `gh` (GitHub) or `tea` (Gitea)
|
||||||
- Unified interface across both platforms
|
- 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
|
## See Also
|
||||||
|
|
||||||
- `~/.config/mosaic/guides/CODE-REVIEW.md` — Manual code review checklist
|
- `~/.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/
|
- 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