@zerwiz/ymir 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/brokk.md +10 -9
- package/.agents/agents/forseti-reviewer.md +2 -2
- package/.agents/agents/mimir-planner.md +2 -2
- package/.agents/assets/agents/registry.md +22 -0
- package/.agents/config/agents.yaml +53 -0
- package/.agents/config/{cron.yaml → cron.yaml.example} +5 -3
- package/.agents/config/tailscale-sync.yaml +9 -0
- package/.agents/config/wedge-alarm +3 -0
- package/.agents/migrations/0003-private-data-separation.sh +4 -4
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +6 -1
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +5 -4
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +5 -5
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +59 -4
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +74 -338
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +1 -1
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +5 -3
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +1 -1
- package/.agents/skills/ratatoskr-a2a/SKILL.md +1 -1
- package/.agents/skills/tyr-check/SKILL.md +5 -5
- package/.agents/skills/ymir-host/SKILL.md +1 -1
- package/.agents/state/.wake-queue +0 -0
- package/.agents/state/.wake-queue.seq +1 -0
- package/.agents/state/.watcher-down +1 -0
- package/.agents/state/procevent-inbox/when-hall-button.1.adapter +1 -0
- package/.agents/state/procevent-inbox/when-hall-button.1.handled +0 -0
- package/.agents/state/procevent-inbox/when-hall-button.1.result +8 -0
- package/.agents/state/procevent-inbox/when-hall-button.2.adapter +1 -0
- package/.agents/state/procevent-inbox/when-hall-button.2.handled +0 -0
- package/.agents/state/procevent-inbox/when-hall-button.2.result +5 -0
- package/.agents/state/procevent-inbox/when-odrerir.1.adapter +1 -0
- package/.agents/state/procevent-inbox/when-odrerir.1.handled +0 -0
- package/.agents/state/procevent-inbox/when-odrerir.1.result +8 -0
- package/.agents/state/procevent-inbox/when-odrerir.2.adapter +1 -0
- package/.agents/state/procevent-inbox/when-odrerir.2.handled +0 -0
- package/.agents/state/procevent-inbox/when-odrerir.2.result +5 -0
- package/.agents/state/procevent-inbox/when-odrerir.3.adapter +1 -0
- package/.agents/state/procevent-inbox/when-odrerir.3.handled +0 -0
- package/.agents/state/procevent-inbox/when-odrerir.3.result +5 -0
- package/.agents/state/procevent-inbox/when-odrerir.4.adapter +1 -0
- package/.agents/state/procevent-inbox/when-odrerir.4.handled +0 -0
- package/.agents/state/procevent-inbox/when-odrerir.4.result +8 -0
- package/.agents/state/procevent-inbox/when-odrerir.5.adapter +1 -0
- package/.agents/state/procevent-inbox/when-odrerir.5.handled +0 -0
- package/.agents/state/procevent-inbox/when-odrerir.5.result +8 -0
- package/.agents/state/procevent-inbox/when-sessrumnir-cloth.1.adapter +1 -0
- package/.agents/state/procevent-inbox/when-sessrumnir-cloth.1.handled +0 -0
- package/.agents/state/procevent-inbox/when-sessrumnir-cloth.1.result +8 -0
- package/AGENTS.md +17 -10
- package/CHANGELOG.md +129 -0
- package/bin/a2a-mcp.sh +9 -2
- package/bin/app-lib.sh +8 -1
- package/bin/docs-guard.sh +1 -1
- package/bin/erindi-brief.sh +1 -1
- package/bin/hamr-harness.sh +1 -1
- package/bin/hoard-lib.sh +10 -0
- package/bin/mimir-bridge.py +8 -1
- package/bin/mimir-bridge.sh +7 -1
- package/bin/mimir-reflect.sh +8 -1
- package/bin/nornir-cron-start.sh +21 -1
- package/bin/nornir-job-bragi-scrape.sh +116 -0
- package/bin/nornir-job-daily-briefing.sh +7 -7
- package/bin/nornir-job-forgejo-git.sh +87 -0
- package/bin/nornir-job-observer.sh +5 -4
- package/bin/rodd-operational-input.sh +1 -1
- package/bin/saga-session-start.sh +4 -3
- package/bin/sessrumnir.sh +11 -0
- package/bin/syn-arm-pretool-check.sh +1 -1
- package/bin/syn-cd-pretool-check.sh +1 -1
- package/bin/syn-watch-arm.sh +1 -1
- package/bin/vor-crew-state.sh +1 -1
- package/bin/ymir-install.sh +47 -2
- package/bin/ymir-marketing-stack.sh +323 -0
- package/docs/runbooks/models.md +1 -1
- package/docs/workspaces.md +4 -0
- package/package.json +10 -4
- package/scripts/electron.sh +2 -2
- package/.agents/bus/messages.json +0 -1
- package/.agents/config/agents.machine.example.yaml +0 -16
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# nornir-job-forgejo-git.sh — the local git round: issues and PRs on the forge.
|
|
3
|
+
#
|
|
4
|
+
# The Forgejo door (forgejo.zerwiz.org → the server's :3030, or a locally
|
|
5
|
+
# provisioned forge via bin/ymir-marketing-stack.sh --with-forgejo) holds the
|
|
6
|
+
# Allfather's issue-to-PR loop. Each morning this job reads the forge's open
|
|
7
|
+
# issues, lands a public-safe digest in the hoard, and carves a Rune with the
|
|
8
|
+
# tally — so a broken door or an accumulating queue is seen at sunrise, never
|
|
9
|
+
# found by accident.
|
|
10
|
+
#
|
|
11
|
+
# The forge address comes from the USER'S home, never the tree (Rule 04/07):
|
|
12
|
+
# $YMIR_HOME/config/forge.env : FORGEJO_URL=... FORGEJO_TOKEN=... (optional)
|
|
13
|
+
# Stateless: read the door → list issues → write one digest → carve one Rune.
|
|
14
|
+
set -u
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
ROOT="${BROKK_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
17
|
+
|
|
18
|
+
if [ -z "${YMIR_HOARD_LIB_LOADED:-}" ]; then
|
|
19
|
+
_yr="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
for _yc in "$_yr/hoard-lib.sh" "$(dirname "$_yr")/bin/hoard-lib.sh"; do
|
|
21
|
+
[ -r "$_yc" ] && { . "$_yc"; YMIR_HOARD_LIB_LOADED=1; break; }
|
|
22
|
+
done
|
|
23
|
+
unset _yr _yc
|
|
24
|
+
fi
|
|
25
|
+
ymir_home_root _HOME
|
|
26
|
+
|
|
27
|
+
FORGE_ENV="$_HOME/config/forge.env"
|
|
28
|
+
[ -r "$FORGE_ENV" ] && . "$FORGE_ENV"
|
|
29
|
+
FORGEJO_URL="${FORGEJO_URL:-}"
|
|
30
|
+
FORGEJO_TOKEN="${FORGEJO_TOKEN:-}"
|
|
31
|
+
|
|
32
|
+
# No address configured — nothing scheduled, exit clean (the same honest shape
|
|
33
|
+
# as a scrape round with no sources).
|
|
34
|
+
if [ -z "$FORGEJO_URL" ]; then
|
|
35
|
+
printf 'forgejo-git: no FORGEJO_URL in %s — issues loop not armed (exit 0)\n' "$FORGE_ENV"
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# The API base: https://host/api/v1 (Forgejo/Gitea compatible).
|
|
40
|
+
API="${FORGEJO_URL%/}/api/v1"
|
|
41
|
+
AUTH=()
|
|
42
|
+
[ -n "$FORGEJO_TOKEN" ] && AUTH=(-H "Authorization: token $FORGEJO_TOKEN")
|
|
43
|
+
|
|
44
|
+
# The door must actually answer — a dead tunnel is reported, not guessed.
|
|
45
|
+
if ! curl -fsS --max-time 15 "${AUTH[@]}" "$API/version" >/dev/null 2>&1; then
|
|
46
|
+
printf 'forgejo-git: door closed at %s (tunnel down?) — issues not listed\n' "$FORGEJO_URL"
|
|
47
|
+
# shellcheck source=bin/runes-append.sh
|
|
48
|
+
. "$ROOT/bin/runes-append.sh"
|
|
49
|
+
runes_append "forgejo" "git.door-down" --realm "${BROKK_REALM:-}" \
|
|
50
|
+
--message "Forgejo door closed at $FORGEJO_URL — tunnel or service down" >/dev/null 2>&1 || true
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# All repos' open issues (the forge API lists them per repo; we query the
|
|
55
|
+
# user's token scope: /repos/issues/search?type=issues&state=open).
|
|
56
|
+
ISSUES_JSON="$(curl -fsS --max-time 25 "${AUTH[@]}" \
|
|
57
|
+
"$API/repos/issues/search?type=issues&state=open&limit=50" 2>/dev/null)" || ISSUES_JSON="[]"
|
|
58
|
+
|
|
59
|
+
COUNT=$(printf '%s' "$ISSUES_JSON" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null || echo 0)
|
|
60
|
+
ISSUES="$(printf '%s' "$ISSUES_JSON" | python3 -c "
|
|
61
|
+
import json,sys
|
|
62
|
+
try:
|
|
63
|
+
d=json.load(sys.stdin)
|
|
64
|
+
for i in d[:15]:
|
|
65
|
+
repo=(i.get('repository') or {}).get('full_name','?')
|
|
66
|
+
print('- [%s] %s (@%s)' % (repo, i.get('title','?')[:90], i.get('user',{}).get('login','?')))
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
" 2>/dev/null)"
|
|
70
|
+
|
|
71
|
+
TODAY="$(date -u +%Y-%m-%d)"
|
|
72
|
+
DIGEST="$_HOME/memory/daily/forgejo-$TODAY.md"
|
|
73
|
+
mkdir -p "$(dirname "$DIGEST")"
|
|
74
|
+
{
|
|
75
|
+
printf '# Forgejo issues — %s\n\n' "$TODAY"
|
|
76
|
+
printf '%s open (limit 50 scanned; first 15 shown)\n\n' "$COUNT"
|
|
77
|
+
printf '%s\n' "$ISSUES"
|
|
78
|
+
} >"$DIGEST"
|
|
79
|
+
|
|
80
|
+
printf 'forgejo-git: issues=%s digest=%s\n' "$COUNT" "$DIGEST"
|
|
81
|
+
|
|
82
|
+
# shellcheck source=bin/runes-append.sh
|
|
83
|
+
. "$ROOT/bin/runes-append.sh"
|
|
84
|
+
runes_append "forgejo" "git.issues" --realm "${BROKK_REALM:-}" \
|
|
85
|
+
--message "forgejo issues round: $COUNT open -> $DIGEST" >/dev/null \
|
|
86
|
+
|| printf 'forgejo-git: rune append failed (non-fatal)\n'
|
|
87
|
+
exit 0
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Read-only observation of the Ymir runtime into Runes. This job touches nothing
|
|
5
5
|
# outside Ymir; every source below lives inside the repo (or the external worktree
|
|
6
6
|
# root, which is read-only):
|
|
7
|
-
# docs/masterplan.md
|
|
7
|
+
# hodd/docs/masterplan.md open forge orders
|
|
8
8
|
# .agents/agents/*.md the agent roster
|
|
9
9
|
# .agents/memory/well/ the well (episodes)
|
|
10
10
|
# workspace/memory/runes_audit.md the ledger
|
|
@@ -32,10 +32,11 @@ WORKTREE_ROOT="${BROKK_YGGDRASIL_ROOT:-$HOME/.treehouse}"
|
|
|
32
32
|
YMIR_HOME="${YMIR_HOME:-$HOME/Documents/Ymir}"
|
|
33
33
|
SMIDJA_DB="${YMIR_HOME:+$YMIR_HOME/smidja/smidja.db}"
|
|
34
34
|
[ -z "${SMIDJA_DB:-}" ] || [ ! -f "$SMIDJA_DB" ] && SMIDJA_DB="$ROOT/apps/smidja/smidja_data/smidja.db"
|
|
35
|
-
MASTERPLAN="$
|
|
36
|
-
WELL_DIR="$
|
|
35
|
+
MASTERPLAN="${BROKK_MASTERPLAN:-${YMIR_HOME:-$HOME/Documents/Ymir}/hodd/docs/masterplan.md}"
|
|
36
|
+
WELL_DIR="${BROKK_WELL_DIR:-${YMIR_HOME:+$YMIR_HOME/hodd/memory/well}}"
|
|
37
|
+
[ -n "$WELL_DIR" ] && [ -d "$WELL_DIR" ] || WELL_DIR="$ROOT/.agents/memory/well"
|
|
37
38
|
AGENTS_DIR="$ROOT/.agents/agents"
|
|
38
|
-
RUNES_LEDGER="${YMIR_HOME:+$YMIR_HOME/memory/runes_audit.md}"
|
|
39
|
+
RUNES_LEDGER="${YMIR_HOME:+$YMIR_HOME/hodd/memory/runes_audit.md}"
|
|
39
40
|
[ -z "${RUNES_LEDGER:-}" ] || [ ! -r "$RUNES_LEDGER" ] && RUNES_LEDGER="$ROOT/workspace/memory/runes_audit.md"
|
|
40
41
|
OBS_LOG="$STATE/observer.log"
|
|
41
42
|
OBS_LAST="$STATE/observer.last"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Rödd ("voice") is the structured message wire between the Brokk primary and
|
|
5
5
|
# Ymir's workers. Ported from the upstream agent-distro reference for the Brokk
|
|
6
|
-
# distro runtime (
|
|
6
|
+
# distro runtime (memory/plans/core/29-brokk-distro-runtime.md). This file is both a
|
|
7
7
|
# source-safe shell library and the cross-language CLI used by the .pi / .opencode
|
|
8
8
|
# integrations.
|
|
9
9
|
#
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Sága ("the seeress", who sees all that happens) collapses the Brokk runtime's
|
|
5
5
|
# session start into ONE ordered digest, so a harness opens in one or two turns
|
|
6
6
|
# instead of many reads. Ported from the upstream agent-distro reference for plan 29
|
|
7
|
-
# (
|
|
7
|
+
# (memory/plans/core/29-brokk-distro-runtime.md).
|
|
8
8
|
#
|
|
9
9
|
# Stages, in order:
|
|
10
10
|
# 1. lock acquire the per-home session lock first
|
|
@@ -109,8 +109,9 @@ if [ -d "$STATE" ]; then
|
|
|
109
109
|
else
|
|
110
110
|
printf 'task metadata records: 0\n'
|
|
111
111
|
fi
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
MP="${BROKK_MASTERPLAN:-${YMIR_HOME:-$HOME/Documents/Ymir}/hodd/docs/masterplan.md}"
|
|
113
|
+
if [ -r "$MP" ]; then
|
|
114
|
+
open=$(grep -c '^- Status: ADDED' "$MP" 2>/dev/null || echo 0)
|
|
114
115
|
printf 'open forge orders: %s\n' "${open:-0}"
|
|
115
116
|
fi
|
|
116
117
|
|
package/bin/sessrumnir.sh
CHANGED
|
@@ -42,6 +42,17 @@ APP="$APP_SESSRUMNIR"
|
|
|
42
42
|
PID_FILE="$YMIR_STATE_DIR/sessrumnir.pid"
|
|
43
43
|
LOG_FILE="$YMIR_STATE_DIR/sessrumnir.log"
|
|
44
44
|
|
|
45
|
+
# npm gates install scripts, so the Electron runtime a seat needs is often absent
|
|
46
|
+
# after a global install — the window then cannot open and says nothing useful.
|
|
47
|
+
# Mend it before refusing (the same lesson as scripts/electron.sh).
|
|
48
|
+
repair_runtime() {
|
|
49
|
+
local dir="$1"
|
|
50
|
+
[ -f "$dir/node_modules/electron/path.txt" ] && return 0
|
|
51
|
+
command -v npm >/dev/null 2>&1 || return 1
|
|
52
|
+
echo "Sessrúmnir — its Electron runtime is absent; fetching it (this is the gated postinstall)…" >&2
|
|
53
|
+
( cd "$dir" && npm rebuild electron >/dev/null 2>&1 ) && [ -f "$dir/node_modules/electron/path.txt" ]
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
case "${1-}" in -v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;; -h|--help|"") sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; esac
|
|
46
57
|
ACTION="${1:-start}"; shift || true
|
|
47
58
|
WORKSPACE="${1-}"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# the extension. Reading it, or merely checking its syntax, is fine.
|
|
9
9
|
#
|
|
10
10
|
# Exit: 0 = allow, 2 = block (stderr carries the reason).
|
|
11
|
-
# Owner: plan 29 (
|
|
11
|
+
# Owner: plan 29 (memory/plans/core/29-brokk-distro-runtime.md).
|
|
12
12
|
set -u
|
|
13
13
|
|
|
14
14
|
command_text=""
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Sýn guards the working directory. v0 is inert (always allow); the contract
|
|
5
5
|
# exists so the Sýn Pi extension can deny a bash invocation that would strand
|
|
6
6
|
# the primary outside its checkout. Owner: plan 29
|
|
7
|
-
# (
|
|
7
|
+
# (memory/plans/core/29-brokk-distro-runtime.md).
|
|
8
8
|
#
|
|
9
9
|
# Usage: syn-cd-pretool-check.sh --command <bash command>
|
|
10
10
|
# Exit: 0 = allow, 2 = block (stderr carries the reason)
|
package/bin/syn-watch-arm.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# actionable `signal:`/`stale:`/`check:`/`heartbeat:` line when something needs
|
|
6
6
|
# the primary. The Pi extension (gna-pi-watch.ts) owns continuity and re-arms.
|
|
7
7
|
# Ported/trimmed from the upstream agent-distro reference for plan 29
|
|
8
|
-
# (
|
|
8
|
+
# (memory/plans/core/29-brokk-distro-runtime.md).
|
|
9
9
|
#
|
|
10
10
|
# Usage: syn-watch-arm.sh --restart
|
|
11
11
|
# syn-watch-arm.sh --handling-delivered <generation> --watcher-pid <pid>
|
package/bin/vor-crew-state.sh
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# from a tail alone, and is read-only.
|
|
15
15
|
#
|
|
16
16
|
# Ported from the upstream distro crew-state reconciliation for plan 29
|
|
17
|
-
# (
|
|
17
|
+
# (memory/plans/core/29-brokk-distro-runtime.md).
|
|
18
18
|
#
|
|
19
19
|
# Usage: vor-crew-state.sh <task-id>
|
|
20
20
|
# Exits 0 on any successful read regardless of state; exit 2 on a usage error.
|
package/bin/ymir-install.sh
CHANGED
|
@@ -131,7 +131,9 @@ Each row is a phase, a step, its state, and why.
|
|
|
131
131
|
Nothing is deleted. Every step is idempotent.
|
|
132
132
|
PLAN
|
|
133
133
|
printf '\n'
|
|
134
|
-
|
|
134
|
+
# The rendering lives on stderr (the cloth); the TOON on stdout is data and is
|
|
135
|
+
# dropped here. Sending BOTH to /dev/null is how the plan became invisible.
|
|
136
|
+
bash "$SCRIPT_DIR/ymir-plan.sh" --colour >/dev/null || true
|
|
135
137
|
printf '\nProceed with the install? [y/N] '
|
|
136
138
|
read -r reply || reply=""
|
|
137
139
|
case "$reply" in
|
|
@@ -291,6 +293,15 @@ step_home() {
|
|
|
291
293
|
# engine (apps/smidja) is stamped from the cloned factory's templates, exactly
|
|
292
294
|
# as install.py does for a target repo.
|
|
293
295
|
step_apps() {
|
|
296
|
+
# A packaged install has no apps/ of its own on purpose: the surfaces arrive as
|
|
297
|
+
# dependencies. Cloning the repos into node_modules/@zerwiz/ymir/apps/ is how a
|
|
298
|
+
# hollow directory shadowed a real package, so a package tree clones nothing.
|
|
299
|
+
# A CLONE still clones — that is the shape this step exists for.
|
|
300
|
+
case "$ROOT" in
|
|
301
|
+
*/node_modules/*)
|
|
302
|
+
add apps SKIP "a package install — the surfaces are dependencies (npm i -g @zerwiz/ymir)"
|
|
303
|
+
return 0 ;;
|
|
304
|
+
esac
|
|
294
305
|
local reg="$HOARD/identity/projects.yaml"
|
|
295
306
|
if [ ! -r "$reg" ]; then
|
|
296
307
|
add apps SKIP "no registry — no app repos to pull ($reg)"
|
|
@@ -533,7 +544,14 @@ step_sandbox() {
|
|
|
533
544
|
|
|
534
545
|
# ── 5. memory (well + harness MCP) ───────────────────────────────────────────
|
|
535
546
|
step_memory() {
|
|
536
|
-
|
|
547
|
+
# The well is ONE memory and it lives in the hoard — always.
|
|
548
|
+
if [ -n "${ENGRAM_DB:-}" ]; then
|
|
549
|
+
local db="$ENGRAM_DB"
|
|
550
|
+
else
|
|
551
|
+
. "$SCRIPT_DIR/hoard-lib.sh" 2>/dev/null || true
|
|
552
|
+
hoard_memory_store db
|
|
553
|
+
fi
|
|
554
|
+
local mcp=0
|
|
537
555
|
[ "$CHECK" = 0 ] && "$SCRIPT_DIR/mimir-bridge.sh" --start >/dev/null 2>&1 || true
|
|
538
556
|
for f in "$ROOT/opencode.json" "$HOME/.config/opencode/opencode.json" "$HOME/.pi/agent/settings.json" "$HOME/.claude.json" "$HOME/.cursor/mcp.json"; do
|
|
539
557
|
[ -f "$f" ] && grep -q '"engram"' "$f" 2>/dev/null && mcp=$((mcp+1))
|
|
@@ -851,6 +869,33 @@ step_auth; step_invite; step_register
|
|
|
851
869
|
|
|
852
870
|
printf 'install[%d]{step,status,detail}:\n' "${#IDS[@]}"
|
|
853
871
|
for i in "${!IDS[@]}"; do printf ' "%s","%s","%s"\n' "${IDS[$i]}" "${STATUS[$i]}" "${DETAIL[$i]}"; done
|
|
872
|
+
# The same rows, rendered for the eye (stderr) — the TOON above is the data a
|
|
873
|
+
# script reads, this is what a person gets. An install that ends in a raw table
|
|
874
|
+
# has no design, and design here is not decoration: it is whether a reader can
|
|
875
|
+
# see, at a glance, what stands, what warned, and what was skipped and why.
|
|
876
|
+
if [ "${STYLE_ON:-0}" = 1 ]; then
|
|
877
|
+
ok=0; warn=0; skip=0; fail=0
|
|
878
|
+
for s_ in "${STATUS[@]}"; do
|
|
879
|
+
case "$s_" in OK) ok=$((ok+1)) ;; WARN) warn=$((warn+1)) ;; SKIP) skip=$((skip+1)) ;; FAIL) fail=$((fail+1)) ;; esac
|
|
880
|
+
done
|
|
881
|
+
style_rule 56
|
|
882
|
+
if [ "$fail" -gt 0 ]; then
|
|
883
|
+
style_line FAIL "not usable" "$fail step(s) failed — nothing below it can be trusted"
|
|
884
|
+
elif [ "$warn" -gt 0 ]; then
|
|
885
|
+
style_line OK "it stands" "$ok steps done · $warn warned · $skip skipped"
|
|
886
|
+
else
|
|
887
|
+
style_line OK "it stands" "$ok steps done · $skip skipped — nothing warned"
|
|
888
|
+
fi
|
|
889
|
+
# the warnings carry the why; they are the rows a person must read
|
|
890
|
+
for i in "${!IDS[@]}"; do
|
|
891
|
+
[ "${STATUS[$i]}" = WARN ] && style_line WARN "${IDS[$i]}" "${DETAIL[$i]}"
|
|
892
|
+
[ "${STATUS[$i]}" = FAIL ] && style_line FAIL "${IDS[$i]}" "${DETAIL[$i]}"
|
|
893
|
+
done
|
|
894
|
+
# and the skips as one line, with their reasons, since they are decisions not faults
|
|
895
|
+
skips=""
|
|
896
|
+
for i in "${!IDS[@]}"; do [ "${STATUS[$i]}" = SKIP ] && skips="$skips ${IDS[$i]}"; done
|
|
897
|
+
[ -n "$skips" ] && style_hint "skipped:$skips — each with its reason in the table above"
|
|
898
|
+
fi
|
|
854
899
|
printf '\nnext: gh auth login · register your projects in hoard/identity/projects.yaml · open http://127.0.0.1:3888/\n'
|
|
855
900
|
[ -n "$INVITE_CODE" ] && printf 'invite: %s — share it to let someone register (bin/ymir-invite.sh list shows what is spent)\n' "$INVITE_CODE"
|
|
856
901
|
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ymir-marketing-stack.sh — the marketing stack, provisionable on ANY computer.
|
|
3
|
+
#
|
|
4
|
+
# Stands up the Allfather's marketing techstack as a user-space Docker Compose
|
|
5
|
+
# set: Mautic (email automation), Postiz (social publishing), Activepieces
|
|
6
|
+
# (workflow), and — with --with-forgejo — Forgejo (local git issues & PRs).
|
|
7
|
+
# Every Eindri (Bragi for marketing, Sindri for git) can run this for another
|
|
8
|
+
# user; nothing here assumes the server. Ports are env-virtualized so two
|
|
9
|
+
# stacks never collide on one host.
|
|
10
|
+
#
|
|
11
|
+
# bin/ymir-marketing-stack.sh up [--with-forgejo] [--dir DIR]
|
|
12
|
+
# [--name NAME] [--host-port P]
|
|
13
|
+
# bin/ymir-marketing-stack.sh status [--dir DIR]
|
|
14
|
+
# bin/ymir-marketing-stack.sh down [--dir DIR]
|
|
15
|
+
# bin/ymir-marketing-stack.sh doors [--dir DIR] # where each service lives
|
|
16
|
+
#
|
|
17
|
+
# Config: env vars (see below) or $YMIR_HOME/config/marketing-stack.env.
|
|
18
|
+
# OSS engines only — Mautic 7 (Apache), Postiz, Activepieces, Forgejo.
|
|
19
|
+
set -u
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
ROOT="${BROKK_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
22
|
+
|
|
23
|
+
# The home the operator chose — one answer, never drift (Rule 04/07).
|
|
24
|
+
if [ -z "${YMIR_HOARD_LIB_LOADED:-}" ]; then
|
|
25
|
+
_yr="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
for _yc in "$_yr/hoard-lib.sh" "$(dirname "$_yr")/bin/hoard-lib.sh"; do
|
|
27
|
+
[ -r "$_yc" ] && { . "$_yc"; YMIR_HOARD_LIB_LOADED=1; break; }
|
|
28
|
+
done
|
|
29
|
+
unset _yr _yc
|
|
30
|
+
fi
|
|
31
|
+
hoard_root _HOME
|
|
32
|
+
hoard_state_dir _STATE
|
|
33
|
+
|
|
34
|
+
# ---- config resolution: env first, then the home's file ---------------------
|
|
35
|
+
CONF_ENV="$_HOME/config/marketing-stack.env"
|
|
36
|
+
[ -r "$CONF_ENV" ] && . "$CONF_ENV"
|
|
37
|
+
|
|
38
|
+
ACTION="${1:-up}"; shift || true
|
|
39
|
+
WITH_FORGEJO=0; DIR="${YMIR_MARKETING_STACK_DIR:-$_STATE/marketing-stack}"
|
|
40
|
+
NAME="${MARKETING_STACK_NAME:-marketing}"
|
|
41
|
+
HOST_PORT="${MARKETING_STACK_HOST_PORT:-8001}"
|
|
42
|
+
while [ $# -gt 0 ]; do
|
|
43
|
+
case "$1" in
|
|
44
|
+
--with-forgejo) WITH_FORGEJO=1; shift ;;
|
|
45
|
+
--dir) DIR=${2-}; [ $# -ge 2 ] && shift 2 || shift ;;
|
|
46
|
+
--name) NAME=${2-}; [ $# -ge 2 ] && shift 2 || shift ;;
|
|
47
|
+
--host-port) HOST_PORT=${2-}; [ $# -ge 2 ] && shift 2 || shift ;;
|
|
48
|
+
*) printf 'error: unknown flag %s\nhelp: ymir-marketing-stack.sh up|status|down|doors [--with-forgejo] [--dir DIR] [--host-port P]\n' "$1" >&2; exit 2 ;;
|
|
49
|
+
esac
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
# ---- secrets: generated once, kept in the home, never inline -----------------
|
|
53
|
+
SECRETS="$DIR/.secrets.env"
|
|
54
|
+
gen_secret() { head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n' | head -c 40; }
|
|
55
|
+
if [ ! -f "$SECRETS" ]; then
|
|
56
|
+
mkdir -p "$DIR"
|
|
57
|
+
{
|
|
58
|
+
echo "MAUTIC_DB_PASSWORD=$(gen_secret)"
|
|
59
|
+
echo "POSTIZ_DB_PASSWORD=$(gen_secret)"
|
|
60
|
+
echo "POSTIZ_JWT_SECRET=$(gen_secret)"
|
|
61
|
+
echo "AP_POSTGRES_PASSWORD=$(gen_secret)"
|
|
62
|
+
echo "FORGEJO_DB_PASSWORD=$(gen_secret)"
|
|
63
|
+
echo "FORGEJO_SECRET_KEY=$(gen_secret)"
|
|
64
|
+
} >"$SECRETS"
|
|
65
|
+
chmod 600 "$SECRETS"
|
|
66
|
+
fi
|
|
67
|
+
. "$SECRETS"
|
|
68
|
+
|
|
69
|
+
MAUTIC_PORT="${MAUTIC_PORT:-$HOST_PORT}"
|
|
70
|
+
POSTIZ_PORT="${POSTIZ_PORT:-$((HOST_PORT + 2))}"
|
|
71
|
+
AP_PORT="${AP_PORT:-$((HOST_PORT + 4))}"
|
|
72
|
+
FORGEJO_PORT="${FORGEJO_PORT:-$((HOST_PORT + 6))}"
|
|
73
|
+
|
|
74
|
+
STRATEGY="compose"
|
|
75
|
+
|
|
76
|
+
# ---- the compose file, rendered fresh each run -------------------------------
|
|
77
|
+
render_compose() {
|
|
78
|
+
local forgejo_services="" forgejo_volumes=""
|
|
79
|
+
if [ "$WITH_FORGEJO" = 1 ]; then
|
|
80
|
+
forgejo_services='
|
|
81
|
+
forgejo:
|
|
82
|
+
image: codeberg.org/forgejo/forgejo:16
|
|
83
|
+
container_name: "'"$NAME"'-forgejo"
|
|
84
|
+
restart: unless-stopped
|
|
85
|
+
environment:
|
|
86
|
+
FORGEJO__server__ROOT_URL: "http://127.0.0.1:'"$FORGEJO_PORT"'/"
|
|
87
|
+
FORGEJO__server__HTTP_PORT: "3000"
|
|
88
|
+
FORGEJO__database__DB_TYPE: postgres
|
|
89
|
+
FORGEJO__database__HOST: forgejo-db
|
|
90
|
+
FORGEJO__database__NAME: forgejo
|
|
91
|
+
FORGEJO__database__USER: forgejo
|
|
92
|
+
FORGEJO__database__PASSWD: '"$FORGEJO_DB_PASSWORD"'
|
|
93
|
+
ports:
|
|
94
|
+
- "127.0.0.1:'"$FORGEJO_PORT"':3000"
|
|
95
|
+
volumes:
|
|
96
|
+
- forgejo-data:/data
|
|
97
|
+
depends_on:
|
|
98
|
+
forgejo-db:
|
|
99
|
+
condition: service_started
|
|
100
|
+
|
|
101
|
+
forgejo-db:
|
|
102
|
+
image: postgres:16-alpine
|
|
103
|
+
container_name: "'"$NAME"'-forgejo-db"
|
|
104
|
+
restart: unless-stopped
|
|
105
|
+
environment:
|
|
106
|
+
POSTGRES_DB: forgejo
|
|
107
|
+
POSTGRES_USER: forgejo
|
|
108
|
+
POSTGRES_PASSWORD: '"$FORGEJO_DB_PASSWORD"'
|
|
109
|
+
volumes:
|
|
110
|
+
- forgejo-db:/var/lib/postgresql/data
|
|
111
|
+
'
|
|
112
|
+
forgejo_volumes=' forgejo-data:
|
|
113
|
+
forgejo-db:'
|
|
114
|
+
fi
|
|
115
|
+
cat >"$DIR/docker-compose.yml" <<EOF
|
|
116
|
+
# generated by ymir-marketing-stack.sh ($(date -u +%Y-%m-%dT%H:%M:%SZ))
|
|
117
|
+
# OSS engines: mautic/mautic:7 · ghcr.io/gitroomhq/postiz-app · ghcr.io/activepieces/activepieces · codeberg.org/forgejo/forgejo
|
|
118
|
+
services:
|
|
119
|
+
${NAME}-mautic-web:
|
|
120
|
+
image: mautic/mautic:7.0.1-apache
|
|
121
|
+
container_name: "${NAME}-mautic-web"
|
|
122
|
+
restart: unless-stopped
|
|
123
|
+
ports:
|
|
124
|
+
- "127.0.0.1:${MAUTIC_PORT}:80"
|
|
125
|
+
environment:
|
|
126
|
+
MAUTIC_DB_HOST: ${NAME}-mautic-db
|
|
127
|
+
MAUTIC_DB_PORT: "3306"
|
|
128
|
+
MAUTIC_DB_DATABASE: mautic
|
|
129
|
+
MAUTIC_DB_USER: mautic
|
|
130
|
+
MAUTIC_DB_PASSWORD: ${MAUTIC_DB_PASSWORD}
|
|
131
|
+
DOCKER_MAUTIC_ROLE: mautic_web
|
|
132
|
+
DOCKER_MAUTIC_RUN_MIGRATIONS: "true"
|
|
133
|
+
volumes:
|
|
134
|
+
- mautic-config:/var/www/html/config
|
|
135
|
+
- mautic-logs:/var/www/html/var/logs
|
|
136
|
+
- mautic-media-files:/var/www/html/docroot/media/files
|
|
137
|
+
- mautic-media-images:/var/www/html/docroot/media/images
|
|
138
|
+
depends_on:
|
|
139
|
+
${NAME}-mautic-db:
|
|
140
|
+
condition: service_healthy
|
|
141
|
+
|
|
142
|
+
${NAME}-mautic-worker:
|
|
143
|
+
image: mautic/mautic:7.0.1-apache
|
|
144
|
+
container_name: "${NAME}-mautic-worker"
|
|
145
|
+
restart: unless-stopped
|
|
146
|
+
environment:
|
|
147
|
+
MAUTIC_DB_HOST: ${NAME}-mautic-db
|
|
148
|
+
MAUTIC_DB_PORT: "3306"
|
|
149
|
+
MAUTIC_DB_DATABASE: mautic
|
|
150
|
+
MAUTIC_DB_USER: mautic
|
|
151
|
+
MAUTIC_DB_PASSWORD: ${MAUTIC_DB_PASSWORD}
|
|
152
|
+
DOCKER_MAUTIC_ROLE: mautic_worker
|
|
153
|
+
DOCKER_MAUTIC_WORKERS_CONSUME_EMAIL: "2"
|
|
154
|
+
DOCKER_MAUTIC_WORKERS_CONSUME_HIT: "2"
|
|
155
|
+
DOCKER_MAUTIC_WORKERS_CONSUME_FAILED: "2"
|
|
156
|
+
volumes:
|
|
157
|
+
- mautic-config:/var/www/html/config
|
|
158
|
+
- mautic-logs:/var/www/html/var/logs
|
|
159
|
+
- mautic-media-files:/var/www/html/docroot/media/files
|
|
160
|
+
- mautic-media-images:/var/www/html/docroot/media/images
|
|
161
|
+
depends_on:
|
|
162
|
+
${NAME}-mautic-db:
|
|
163
|
+
condition: service_healthy
|
|
164
|
+
|
|
165
|
+
${NAME}-mautic-db:
|
|
166
|
+
image: mariadb:10.11
|
|
167
|
+
container_name: "${NAME}-mautic-db"
|
|
168
|
+
restart: unless-stopped
|
|
169
|
+
command: >
|
|
170
|
+
--character-set-server=utf8mb4
|
|
171
|
+
--collation-server=utf8mb4_unicode_ci
|
|
172
|
+
environment:
|
|
173
|
+
MYSQL_DATABASE: mautic
|
|
174
|
+
MYSQL_USER: mautic
|
|
175
|
+
MYSQL_PASSWORD: ${MAUTIC_DB_PASSWORD}
|
|
176
|
+
MYSQL_ROOT_PASSWORD: ${MAUTIC_DB_PASSWORD}
|
|
177
|
+
volumes:
|
|
178
|
+
- mautic-db:/var/lib/mysql
|
|
179
|
+
healthcheck:
|
|
180
|
+
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
181
|
+
interval: 10s
|
|
182
|
+
timeout: 5s
|
|
183
|
+
retries: 6
|
|
184
|
+
|
|
185
|
+
${NAME}-postiz:
|
|
186
|
+
image: ghcr.io/gitroomhq/postiz-app:latest
|
|
187
|
+
container_name: "${NAME}-postiz"
|
|
188
|
+
restart: always
|
|
189
|
+
environment:
|
|
190
|
+
MAIN_URL: "http://127.0.0.1:${POSTIZ_PORT}"
|
|
191
|
+
FRONTEND_URL: "http://127.0.0.1:${POSTIZ_PORT}"
|
|
192
|
+
NEXT_PUBLIC_BACKEND_URL: "http://127.0.0.1:${POSTIZ_PORT}/api"
|
|
193
|
+
JWT_SECRET: ${POSTIZ_JWT_SECRET}
|
|
194
|
+
DATABASE_URL: "postgresql://postiz-user:${POSTIZ_DB_PASSWORD}@${NAME}-postiz-db:5432/postiz-db-local"
|
|
195
|
+
REDIS_URL: "redis://${NAME}-postiz-redis:6379"
|
|
196
|
+
BACKEND_INTERNAL_URL: "http://localhost:3000"
|
|
197
|
+
IS_GENERAL: "true"
|
|
198
|
+
DISABLE_REGISTRATION: "false"
|
|
199
|
+
RUN_CRON: "true"
|
|
200
|
+
STORAGE_PROVIDER: "local"
|
|
201
|
+
UPLOAD_DIRECTORY: "/uploads"
|
|
202
|
+
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
|
|
203
|
+
ports:
|
|
204
|
+
- "127.0.0.1:${POSTIZ_PORT}:5000"
|
|
205
|
+
depends_on:
|
|
206
|
+
- ${NAME}-postiz-db
|
|
207
|
+
- ${NAME}-postiz-redis
|
|
208
|
+
|
|
209
|
+
${NAME}-postiz-db:
|
|
210
|
+
image: postgres:17-alpine
|
|
211
|
+
container_name: "${NAME}-postiz-db"
|
|
212
|
+
restart: always
|
|
213
|
+
environment:
|
|
214
|
+
POSTGRES_DB: postiz-db-local
|
|
215
|
+
POSTGRES_USER: postiz-user
|
|
216
|
+
POSTGRES_PASSWORD: ${POSTIZ_DB_PASSWORD}
|
|
217
|
+
volumes:
|
|
218
|
+
- postiz-db:/var/lib/postgresql/data
|
|
219
|
+
|
|
220
|
+
${NAME}-postiz-redis:
|
|
221
|
+
image: redis:7-alpine
|
|
222
|
+
container_name: "${NAME}-postiz-redis"
|
|
223
|
+
restart: always
|
|
224
|
+
volumes:
|
|
225
|
+
- postiz-redis:/data
|
|
226
|
+
|
|
227
|
+
${NAME}-activepieces:
|
|
228
|
+
image: ghcr.io/activepieces/activepieces:latest
|
|
229
|
+
container_name: "${NAME}-activepieces"
|
|
230
|
+
restart: unless-stopped
|
|
231
|
+
ports:
|
|
232
|
+
- "127.0.0.1:${AP_PORT}:80"
|
|
233
|
+
environment:
|
|
234
|
+
AP_FRONTEND_URL: "http://127.0.0.1:${AP_PORT}"
|
|
235
|
+
AP_ENGINE_EXECUTABLE_PATH: "dist/packages/engine/main.js"
|
|
236
|
+
AP_DB_TYPE: POSTGRES
|
|
237
|
+
AP_DB_USERNAME: ap_user
|
|
238
|
+
AP_DB_PASSWORD: ${AP_POSTGRES_PASSWORD}
|
|
239
|
+
AP_DB_DATABASE: activepieces
|
|
240
|
+
AP_DB_HOST: ${NAME}-activepieces-db
|
|
241
|
+
AP_DB_PORT: "5432"
|
|
242
|
+
AP_REDIS_HOST: ${NAME}-activepieces-redis
|
|
243
|
+
AP_REDIS_PORT: "6379"
|
|
244
|
+
AP_QUEUE_MODE: REDIS
|
|
245
|
+
volumes:
|
|
246
|
+
- activepieces-cache:/usr/src/app/cache
|
|
247
|
+
depends_on:
|
|
248
|
+
${NAME}-activepieces-db:
|
|
249
|
+
condition: service_healthy
|
|
250
|
+
${NAME}-activepieces-redis:
|
|
251
|
+
condition: service_started
|
|
252
|
+
|
|
253
|
+
${NAME}-activepieces-db:
|
|
254
|
+
image: postgres:16-alpine
|
|
255
|
+
container_name: "${NAME}-activepieces-db"
|
|
256
|
+
restart: unless-stopped
|
|
257
|
+
environment:
|
|
258
|
+
POSTGRES_DB: activepieces
|
|
259
|
+
POSTGRES_USER: ap_user
|
|
260
|
+
POSTGRES_PASSWORD: ${AP_POSTGRES_PASSWORD}
|
|
261
|
+
volumes:
|
|
262
|
+
- activepieces-db:/var/lib/postgresql/data
|
|
263
|
+
healthcheck:
|
|
264
|
+
test: ["CMD-SHELL", "pg_isready -U ap_user"]
|
|
265
|
+
interval: 10s
|
|
266
|
+
start_period: 20s
|
|
267
|
+
|
|
268
|
+
${NAME}-activepieces-redis:
|
|
269
|
+
image: redis:7-alpine
|
|
270
|
+
container_name: "${NAME}-activepieces-redis"
|
|
271
|
+
restart: unless-stopped
|
|
272
|
+
volumes:
|
|
273
|
+
- activepieces-redis:/data
|
|
274
|
+
${forgejo_services}
|
|
275
|
+
volumes:
|
|
276
|
+
mautic-config:
|
|
277
|
+
mautic-logs:
|
|
278
|
+
mautic-media-files:
|
|
279
|
+
mautic-media-images:
|
|
280
|
+
mautic-db:
|
|
281
|
+
postiz-db:
|
|
282
|
+
postiz-redis:
|
|
283
|
+
activepieces-cache:
|
|
284
|
+
activepieces-db:
|
|
285
|
+
activepieces-redis:
|
|
286
|
+
${forgejo_volumes}
|
|
287
|
+
EOF
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
engine_up() {
|
|
291
|
+
render_compose
|
|
292
|
+
(cd "$DIR" && docker compose up -d 2>&1 | tail -4)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
status() {
|
|
296
|
+
if [ ! -f "$DIR/docker-compose.yml" ]; then
|
|
297
|
+
printf 'marketing-stack[1]{state,dir}:\n "not-provisioned","%s"\n' "$DIR"
|
|
298
|
+
return 0
|
|
299
|
+
fi
|
|
300
|
+
(cd "$DIR" && docker compose ps --format 'table {{.Name}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null | head -14)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
doors() {
|
|
304
|
+
printf 'marketing-stack[1]{service,url}:\n'
|
|
305
|
+
printf ' "Mautic (email automation)",http://127.0.0.1:%s\n' "$MAUTIC_PORT"
|
|
306
|
+
printf ' "Postiz (social publishing)",http://127.0.0.1:%s\n' "$POSTIZ_PORT"
|
|
307
|
+
printf ' "Activepieces (workflow)",http://127.0.0.1:%s\n' "$AP_PORT"
|
|
308
|
+
[ "$WITH_FORGEJO" = 1 ] && printf ' "Forgejo (git · issues · PRs)",http://127.0.0.1:%s\n' "$FORGEJO_PORT"
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
case "$ACTION" in
|
|
312
|
+
up) engine_up ;;
|
|
313
|
+
status) status ;;
|
|
314
|
+
down) [ -f "$DIR/docker-compose.yml" ] && (cd "$DIR" && docker compose down 2>&1 | tail -2) || printf 'marketing-stack: nothing provisioned at %s\n' "$DIR" ;;
|
|
315
|
+
doors) doors ;;
|
|
316
|
+
*) printf 'error: unknown action %s\nhelp: ymir-marketing-stack.sh up|status|down|doors\n' "$ACTION" >&2; exit 2 ;;
|
|
317
|
+
esac
|
|
318
|
+
|
|
319
|
+
# ---- carve the Rune (append-only ledger, in the hoard) -----------------------
|
|
320
|
+
# shellcheck source=bin/runes-append.sh
|
|
321
|
+
. "$ROOT/bin/runes-append.sh" 2>/dev/null || true
|
|
322
|
+
runes_append "marketing" "stack.$ACTION" --realm "${BROKK_REALM:-}" \
|
|
323
|
+
--message "marketing stack $ACTION (Mautic:$MAUTIC_PORT Postiz:$POSTIZ_PORT Activepieces:$AP_PORT${WITH_FORGEJO:+ Forgejo:$FORGEJO_PORT}) at $DIR" >/dev/null 2>&1 || true
|
package/docs/runbooks/models.md
CHANGED
|
@@ -56,7 +56,7 @@ bin/agent-run.sh hnoss "design a hero section"
|
|
|
56
56
|
|
|
57
57
|
Keep a per-machine overlay `config/agents.<hostname>.yaml`; it deep-merges over
|
|
58
58
|
the base. Example: `config/agents.machine.example.yaml`. Override the host with
|
|
59
|
-
`YMIR_AGENTS_MACHINE=omarchy
|
|
59
|
+
`YMIR_AGENTS_MACHINE=omarchy`.
|
|
60
60
|
|
|
61
61
|
## Hosted models
|
|
62
62
|
|
package/docs/workspaces.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
> question — where a *project* lives — had no single answer. Two rules decide it:
|
|
7
7
|
> private data is never inside the checkout (Rule 04), and a domain is not a
|
|
8
8
|
> house (Rule 01/03).
|
|
9
|
+
>
|
|
10
|
+
> **2026-09-17:** the roots settled — workspaces now live under
|
|
11
|
+
> `$YMIR_HOME/hodd/workspaces/` (marketing/ · personal/ · work/); this page
|
|
12
|
+
> stays the map, the hoard is authoritative.
|
|
9
13
|
|
|
10
14
|
## The one idea
|
|
11
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerwiz/ymir",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Ymir \u2014 the single-tenant agent operating system. Omarchy-first, host-aware, self-healing.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/zerwiz/ymir",
|
|
@@ -60,9 +60,15 @@
|
|
|
60
60
|
"compliance": "bash .agents/skills/galdr-ymirsystem/scripts/compliance-check.sh"
|
|
61
61
|
},
|
|
62
62
|
"optionalDependencies": {
|
|
63
|
-
"@zerwiz/hlidskjalf": "
|
|
64
|
-
"@zerwiz/odrerir": "
|
|
63
|
+
"@zerwiz/hlidskjalf": "0.1.1",
|
|
64
|
+
"@zerwiz/odrerir": "0.1.1",
|
|
65
65
|
"@zerwiz/sessrumnir": "0.1.9",
|
|
66
66
|
"@zerwiz/smidja-factory": "^0.1.0"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@zerwiz/hlidskjalf": "^0.1.1",
|
|
70
|
+
"@zerwiz/odrerir": "^0.1.1",
|
|
71
|
+
"@zerwiz/sessrumnir": "^0.1.10",
|
|
72
|
+
"@zerwiz/smidja-factory": "^0.1.2"
|
|
67
73
|
}
|
|
68
|
-
}
|
|
74
|
+
}
|
package/scripts/electron.sh
CHANGED
|
@@ -289,10 +289,10 @@ if [ "$VIEW" = both ]; then
|
|
|
289
289
|
start_one odrerir
|
|
290
290
|
sleep 2
|
|
291
291
|
for v in "${VIEWS[@]}"; do
|
|
292
|
-
if is_running "$v"; then printf 'electron[1]{view,state,pid,url}:\n "%s","up",%s,"%s"\n' "$v" "$(pid_of "$v")" "$(view_host "$v")"; else printf 'error: electron %s failed to start; see %s\n' "$v" "$
|
|
292
|
+
if is_running "$v"; then printf 'electron[1]{view,state,pid,url}:\n "%s","up",%s,"%s"\n' "$v" "$(pid_of "$v")" "$(view_host "$v")"; else printf 'error: electron %s failed to start; see %s\n' "$v" "$(log_file "$v")" >&2; exit 1; fi
|
|
293
293
|
done
|
|
294
294
|
else
|
|
295
295
|
start_one "$VIEW"
|
|
296
296
|
sleep 2
|
|
297
|
-
if is_running "$VIEW"; then printf 'electron[1]{view,state,pid,url}:\n "%s","up",%s,"%s"\n' "$VIEW" "$(pid_of "$VIEW")" "$(view_host "$VIEW")"; else printf 'error: electron failed to start; see %s\n' "$
|
|
297
|
+
if is_running "$VIEW"; then printf 'electron[1]{view,state,pid,url}:\n "%s","up",%s,"%s"\n' "$VIEW" "$(pid_of "$VIEW")" "$(view_host "$VIEW")"; else printf 'error: electron failed to start; see %s\n' "$(log_file "$VIEW")" >&2; exit 1; fi
|
|
298
298
|
fi
|