@zerwiz/ymir 0.1.5 → 0.1.8
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/huginn-researcher.md +1 -1
- package/.agents/agents/kvasir-scout.md +1 -1
- package/.agents/agents/sindri-developer.md +1 -1
- package/.agents/backend/README.md +74 -0
- package/.agents/backend/model-bridge.py +74 -4
- package/.agents/config/cron.yaml +2 -0
- package/.agents/config/eindri-dispatch.json +6 -6
- package/.agents/migrations/0003-private-data-separation.sh +11 -1
- package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
- package/.agents/skills/README.md +1 -1
- package/.agents/skills/galdr-ymirsystem/SKILL.md +2 -1
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +73 -10
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +44 -1
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +290 -24
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +3 -1
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +39 -1
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +4 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +7 -2
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +1 -1
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +2 -2
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +16 -11
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +147 -4
- package/.agents/skills/groa-update/SKILL.md +20 -0
- package/.agents/skills/hamr-adapters/SKILL.md +2 -2
- package/.agents/skills/lifecycle/smoke_test.sh +7 -2
- package/.agents/skills/ymir-host/SKILL.md +2 -1
- package/.agents/skills/ymir-host/assets/install.md +69 -0
- package/AGENTS.md +78 -34
- package/CHANGELOG.md +1362 -1
- package/README.md +1 -1
- package/RULES/02-agents.md +2 -2
- package/RULES/04-hoard.md +32 -0
- package/RULES/06-append-only.md +50 -0
- package/RULES/07-config.md +56 -0
- package/RULES/09-electron.md +58 -0
- package/RULES/README.md +2 -0
- package/bin/a2a-mcp.sh +42 -7
- package/bin/agents-config.sh +30 -7
- package/bin/apodex-smoke-test.sh +14 -1
- package/bin/bifrost-bridge.sh +25 -6
- package/bin/bootstrap-macos.sh +6 -1
- package/bin/brokk-send.sh +14 -1
- package/bin/changelog-assemble.sh +154 -0
- package/bin/changelog-guard.sh +39 -7
- package/bin/crash-sense.sh +14 -1
- package/bin/design-check.sh +80 -0
- package/bin/design-icon.sh +156 -0
- package/bin/docs-guard.sh +2 -2
- package/bin/editor-place.sh +33 -15
- package/bin/eindri-acclaim.sh +14 -1
- package/bin/eindri-seat.sh +83 -0
- package/bin/eindri-seen.sh +14 -1
- package/bin/eindri-start.sh +14 -1
- package/bin/einherjar-spawn.sh +9 -3
- package/bin/eir-doctor.sh +89 -1
- package/bin/electron-lib.sh +34 -0
- package/bin/feature-inventory.sh +73 -0
- package/bin/fleet-apply.sh +15 -2
- package/bin/github-deploy.sh +14 -1
- package/bin/gjallarhorn-expose.sh +43 -9
- package/bin/gjallarhorn-notify.sh +14 -1
- package/bin/gjallarhorn-purge.sh +14 -1
- package/bin/gjallarhorn-tunnel.sh +16 -3
- package/bin/groa-update.sh +9 -1
- package/bin/hall-snapshot.sh +21 -2
- package/bin/herdr-run.sh +23 -2
- package/bin/hlidskjalf-agents.sh +172 -0
- package/bin/hlidskjalf-usage.sh +196 -0
- package/bin/hoard-guard.sh +135 -0
- package/bin/hoard-lib.sh +91 -11
- package/bin/hodd.sh +28 -4
- package/bin/huginn-research-worker.sh +14 -1
- package/bin/local-model-lock.sh +22 -3
- package/bin/mimir-bridge.py +33 -0
- package/bin/mimir-bridge.sh +14 -1
- package/bin/mimir-reflect.sh +27 -0
- package/bin/mjollnir-webhook.sh +14 -1
- package/bin/mjollnir.sh +22 -3
- package/bin/model-hardware.sh +17 -3
- package/bin/model-resolve.sh +14 -1
- package/bin/nornir-job-hall-snapshot.sh +28 -0
- package/bin/nornir-job-nsr-compliance.sh +54 -0
- package/bin/npm-publish.sh +136 -0
- package/bin/omarchy-install.sh +18 -5
- package/bin/omarchy-plugins.sh +15 -2
- package/bin/omarchy-sense.sh +14 -1
- package/bin/pi-model-agnostic.sh +71 -0
- package/bin/private-guard.sh +20 -0
- package/bin/project-git.sh +18 -4
- package/bin/public-guard.sh +3 -2
- package/bin/sessrumnir-ensure.sh +1 -1
- package/bin/sessrumnir.sh +16 -3
- package/bin/smidja-board.sh +136 -0
- package/bin/smidja-bootstrap.sh +5 -1
- package/bin/smidja-lib.sh +49 -0
- package/bin/syn-asset-pretool-check.sh +14 -1
- package/bin/syn-spawn-pretool-check.sh +58 -0
- package/bin/tailscale-sync.sh +15 -2
- package/bin/telegram-bot.sh +14 -1
- package/bin/ui-truth-check.sh +75 -0
- package/bin/valknut-load.sh +42 -4
- package/bin/wedge-notify.sh +14 -1
- package/bin/workspace-rag.sh +14 -1
- package/bin/ymir-install.sh +257 -57
- package/bin/ymir-plan.sh +390 -0
- package/bin/ymir-say.sh +14 -1
- package/bin/ymir-setup-auth.sh +12 -1
- package/bin/ymir-style.sh +108 -0
- package/bin/ymir-validate.sh +64 -11
- package/bin/ymir.js +82 -34
- package/docs/design.md +2 -2
- package/docs/lore.md +5 -1
- package/docs/runbooks/agents.md +1 -1
- package/package.json +13 -3
- package/scripts/electron.sh +74 -15
- package/scripts/lower.sh +34 -0
- package/scripts/raise.sh +42 -0
- package/scripts/start.sh +65 -3
- package/.agents/config/agents.yaml +0 -50
- package/.agents/config/tailscale-sync.yaml +0 -27
- package/.agents/memory/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/well/episodes.jsonl +0 -367
- package/.agents/memory/well/workspace.jsonl +0 -27
package/bin/ymir-plan.sh
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ymir-plan.sh — THE INSTALL PLAN. Probe this machine, print what would change,
|
|
3
|
+
# change nothing.
|
|
4
|
+
#
|
|
5
|
+
# The consent a real install asks for used to be a hardcoded paragraph, and a
|
|
6
|
+
# paragraph cannot know the host: it named an Omarchy version on a Mac, promised
|
|
7
|
+
# a workspace tree that already stood, and never mentioned that no application
|
|
8
|
+
# had been installed at all. This is the replacement: a plan COMPUTED from the
|
|
9
|
+
# same host the install will act on, one row per step, each row carrying its
|
|
10
|
+
# state and the reason for it.
|
|
11
|
+
#
|
|
12
|
+
# DO a change will be made
|
|
13
|
+
# SKIP already satisfied — nothing to do
|
|
14
|
+
# DO · (with a note) the step is available but conditional
|
|
15
|
+
# BLOCKED cannot run; the reason names what is missing
|
|
16
|
+
# CONSENT needs the operator's word before it may run
|
|
17
|
+
# INFO a fact about this host, discovered — no change implied
|
|
18
|
+
#
|
|
19
|
+
# Usage:
|
|
20
|
+
# bin/ymir-plan.sh # the plan (TOON)
|
|
21
|
+
# bin/ymir-plan.sh --json # the same, as JSON, for automation
|
|
22
|
+
# bin/ymir-plan.sh --phase 5 # one phase
|
|
23
|
+
# bin/ymir-plan.sh --blocked # only what cannot proceed, and why
|
|
24
|
+
#
|
|
25
|
+
# Exit: 0 the plan was printed (a BLOCKED row is a fact, not a failure), 1 error,
|
|
26
|
+
# 2 usage.
|
|
27
|
+
set -u
|
|
28
|
+
|
|
29
|
+
# --- portability shim: bin/ymir-platform.sh --------------------------------
|
|
30
|
+
if [ -z "${YMIR_PLATFORM_LOADED:-}" ]; then
|
|
31
|
+
_ymir_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
32
|
+
for _ymir_c in "$_ymir_dir/ymir-platform.sh" "$(dirname "$_ymir_dir")/bin/ymir-platform.sh"; do
|
|
33
|
+
[ -r "$_ymir_c" ] && { . "$_ymir_c"; YMIR_PLATFORM_LOADED=1; break; }
|
|
34
|
+
done
|
|
35
|
+
unset _ymir_dir _ymir_c
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
VERSION="1.0.0"
|
|
39
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
40
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
41
|
+
# Whether the home was named explicitly matters: a home that was chosen is not
|
|
42
|
+
# the same as one we defaulted to, and the plan says which.
|
|
43
|
+
YMIR_HOME_WAS_SET=0; [ -n "${YMIR_HOME:-}" ] && YMIR_HOME_WAS_SET=1
|
|
44
|
+
# shellcheck source=bin/hoard-lib.sh
|
|
45
|
+
. "$SCRIPT_DIR/hoard-lib.sh"
|
|
46
|
+
ymir_home_root YMIR_HOME
|
|
47
|
+
hoard_root HOARD
|
|
48
|
+
|
|
49
|
+
JSON=0; COLOUR=0; ONLY_PHASE=""; ONLY_BLOCKED=0
|
|
50
|
+
case "${1-}" in
|
|
51
|
+
-v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;;
|
|
52
|
+
-h|--help) sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
53
|
+
esac
|
|
54
|
+
while [ $# -gt 0 ]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--json) JSON=1; shift ;;
|
|
57
|
+
--colour|--color) COLOUR=1; shift ;;
|
|
58
|
+
--phase) ONLY_PHASE="${2-}"; shift 2 ;;
|
|
59
|
+
--blocked) ONLY_BLOCKED=1; shift ;;
|
|
60
|
+
*) printf 'error: unknown flag %s\nhelp: bin/ymir-plan.sh [--json|--colour] [--phase N] [--blocked]\n' "$1" >&2; exit 2 ;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
declare -a P_N P_NAME P_STEP P_STATE P_WHY
|
|
65
|
+
emit() { P_N+=("$1"); P_NAME+=("$2"); P_STEP+=("$3"); P_STATE+=("$4"); P_WHY+=("$5"); }
|
|
66
|
+
|
|
67
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
68
|
+
port_up() { # <port>
|
|
69
|
+
if have curl; then curl -s -o /dev/null --max-time 1 "http://127.0.0.1:$1/" 2>/dev/null && return 0; fi
|
|
70
|
+
if have ss; then ss -ltn 2>/dev/null | grep -q ":$1 " && return 0; fi
|
|
71
|
+
return 1
|
|
72
|
+
}
|
|
73
|
+
# An app is ours by one of two shapes: a source checkout under apps/, or a
|
|
74
|
+
# published package under node_modules/@zerwiz/. Neither is guessed.
|
|
75
|
+
# A surface has a name the operator knows and a PACKAGE has a name npm serves;
|
|
76
|
+
# they are not always the same (the smithy is `smidja` to us and
|
|
77
|
+
# `@zerwiz/smidja-factory` on the registry). <surface> [<package>]
|
|
78
|
+
app_dir() {
|
|
79
|
+
local n="$1" pkg="${2:-$1}" c
|
|
80
|
+
for c in "$ROOT/apps/$n" "$ROOT/node_modules/@zerwiz/$pkg"; do
|
|
81
|
+
[ -d "$c" ] && { printf '%s' "$c"; return 0; }
|
|
82
|
+
done
|
|
83
|
+
return 1
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# ── phase 0 · resolve — where this runs, and against what ────────────────────
|
|
87
|
+
resolve_phase() {
|
|
88
|
+
local os="" desk="terminal"
|
|
89
|
+
os="$(ymir_os 2>/dev/null || printf unknown)"
|
|
90
|
+
[ -n "${WAYLAND_DISPLAY:-}" ] && desk="wayland"
|
|
91
|
+
[ -n "${DISPLAY:-}" ] && desk="x11"
|
|
92
|
+
[ -d /usr/share/omarchy ] && desk="$desk · omarchy first-class"
|
|
93
|
+
case "$os" in
|
|
94
|
+
wsl) desk="$desk · wsl2" ;;
|
|
95
|
+
msys) desk="$desk · msys (best-effort)" ;;
|
|
96
|
+
esac
|
|
97
|
+
emit 0 resolve os INFO "$os · $desk"
|
|
98
|
+
|
|
99
|
+
local eng=""
|
|
100
|
+
if command -v ymir_container_engine >/dev/null 2>&1; then eng="$(ymir_container_engine_name 2>/dev/null || true)"; fi
|
|
101
|
+
[ -z "$eng" ] && have docker && eng=docker
|
|
102
|
+
[ -z "$eng" ] && have podman && eng=podman
|
|
103
|
+
if [ -n "$eng" ]; then emit 0 resolve engine INFO "$eng"
|
|
104
|
+
else emit 0 resolve engine BLOCKED "no docker or podman on PATH — the Utgard image cannot be built"; fi
|
|
105
|
+
|
|
106
|
+
case "$ROOT" in
|
|
107
|
+
*/node_modules/*) emit 0 resolve code INFO "$ROOT — a package install (read-only at runtime)" ;;
|
|
108
|
+
*) emit 0 resolve code INFO "$ROOT — a source checkout" ;;
|
|
109
|
+
esac
|
|
110
|
+
|
|
111
|
+
case "$HOARD" in
|
|
112
|
+
"$ROOT"/*) emit 0 resolve home BLOCKED "the home ($HOARD) sits inside the code tree — private data would live in the package" ;;
|
|
113
|
+
*) emit 0 resolve home INFO "$HOARD (the home = $YMIR_HOME)" ;;
|
|
114
|
+
esac
|
|
115
|
+
|
|
116
|
+
# The home is the OPERATOR's to choose. Until they have, it is only a default,
|
|
117
|
+
# and the install will ask — so the plan says so rather than pretending.
|
|
118
|
+
local rec=""
|
|
119
|
+
ymir_home_record rec
|
|
120
|
+
if [ -n "$rec" ]; then
|
|
121
|
+
emit 0 resolve chosen SKIP "the home was chosen at installation: $rec"
|
|
122
|
+
elif [ "$YMIR_HOME_WAS_SET" = 1 ]; then
|
|
123
|
+
emit 0 resolve chosen SKIP "the home is set by \$YMIR_HOME: $YMIR_HOME"
|
|
124
|
+
else
|
|
125
|
+
emit 0 resolve chosen CONSENT "no home chosen yet — the install asks, then records it (default $YMIR_HOME_DEFAULT)"
|
|
126
|
+
fi
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# ── phase 1 · code — the tree itself, and its purity ────────────────────────
|
|
130
|
+
code_phase() {
|
|
131
|
+
local n=0
|
|
132
|
+
[ -x "$ROOT/bin/ymir-install.sh" ] && n=$(find "$ROOT/bin" -maxdepth 1 -name '*.sh' 2>/dev/null | wc -l)
|
|
133
|
+
if [ "$n" -gt 0 ] && [ -d "$ROOT/.agents/skills" ] && [ -d "$ROOT/RULES" ]; then
|
|
134
|
+
emit 1 code integrity SKIP "tree intact — $n scripts, .agents/skills, RULES/"
|
|
135
|
+
else
|
|
136
|
+
emit 1 code integrity BLOCKED "bin/, .agents/skills or RULES/ is missing — reinstall the package or re-clone"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# The law this row enforces: the package is the code that RUNS the programs.
|
|
140
|
+
# Everything the operator owns — their records, their state, their settings,
|
|
141
|
+
# their credentials — lives in the home they chose. A packaged install replaces
|
|
142
|
+
# its tree on upgrade, so anything of theirs kept here is kept at its peril.
|
|
143
|
+
# A settings file GIT TRACKS is not theirs: that is the distro's shipped
|
|
144
|
+
# default, and it stays where the code ships it.
|
|
145
|
+
local leaked="" f n
|
|
146
|
+
n="$(ls -A "$ROOT/data" 2>/dev/null | grep -vc '^\.gitkeep$' || true)"
|
|
147
|
+
[ -d "$ROOT/data" ] && [ "${n:-0}" -gt 0 ] && leaked="$leaked data/"
|
|
148
|
+
n="$(ls -A "$ROOT/state" 2>/dev/null | grep -vc '^\.gitkeep$' || true)"
|
|
149
|
+
[ -d "$ROOT/state" ] && [ "${n:-0}" -gt 0 ] && leaked="$leaked state/"
|
|
150
|
+
for f in config/agents.yaml config/cron.yaml config/tailscale-sync.yaml config/wedge-alarm .env.local; do
|
|
151
|
+
[ -e "$ROOT/$f" ] || continue
|
|
152
|
+
# `config` is a symlink into .agents/config, and git tracks the REAL path —
|
|
153
|
+
# ask it about the name the index holds, not the link the tree shows.
|
|
154
|
+
local ask="$f"
|
|
155
|
+
case "$f" in config/*) [ -L "$ROOT/config" ] && ask=".agents/config/${f#config/}" ;; esac
|
|
156
|
+
if command -v git >/dev/null 2>&1 && git -C "$ROOT" ls-files --error-unmatch "$ask" >/dev/null 2>&1; then
|
|
157
|
+
continue # the distro ships this one — code, not the operator's own
|
|
158
|
+
fi
|
|
159
|
+
leaked="$leaked $f"
|
|
160
|
+
done
|
|
161
|
+
if [ -n "$leaked" ]; then
|
|
162
|
+
emit 1 code purity DO "the operator's own things sit in the code tree ($leaked) — they belong in $YMIR_HOME, or the next upgrade will erase them"
|
|
163
|
+
else
|
|
164
|
+
emit 1 code purity SKIP "nothing of the operator's is written into the code tree"
|
|
165
|
+
fi
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# ── phase 2 · home — the operator's world, outside the tree ─────────────────
|
|
169
|
+
home_phase() {
|
|
170
|
+
if [ -d "$HOARD/identity" ] && [ -d "$HOARD/secrets" ]; then
|
|
171
|
+
emit 2 home hoard SKIP "hodd/{identity,secrets,memory} present"
|
|
172
|
+
else
|
|
173
|
+
emit 2 home hoard DO "create hodd/{identity,secrets,docs,tenants,memory} — all private data lives here"
|
|
174
|
+
fi
|
|
175
|
+
if [ -d "$YMIR_HOME/workspaces" ]; then
|
|
176
|
+
emit 2 home workspaces SKIP "$YMIR_HOME/workspaces present"
|
|
177
|
+
else
|
|
178
|
+
emit 2 home workspaces DO "create workspaces/{work,personal} and the registries"
|
|
179
|
+
fi
|
|
180
|
+
# Settings are the operator's: they live in the home, never in the package.
|
|
181
|
+
local settings; hoard_settings_dir settings
|
|
182
|
+
if [ -e "$settings/agents.yaml" ]; then
|
|
183
|
+
emit 2 home agents-config SKIP "the agent set is seeded ($settings/agents.yaml)"
|
|
184
|
+
else
|
|
185
|
+
emit 2 home agents-config DO "seed the private agent set into $settings/agents.yaml (harness + model per agent)"
|
|
186
|
+
fi
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# ── phase 3 · runtimes — every one proven by a probe, never assumed ─────────
|
|
190
|
+
runtimes_phase() {
|
|
191
|
+
local missing=""
|
|
192
|
+
for c in git python3; do have "$c" || missing="$missing $c"; done
|
|
193
|
+
if [ -n "$missing" ]; then
|
|
194
|
+
emit 3 runtimes system BLOCKED "missing$missing — a system package (apt/dnf/brew), not user space"
|
|
195
|
+
else
|
|
196
|
+
emit 3 runtimes system SKIP "git and python3 present"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
local user=""
|
|
200
|
+
for c in bun uv; do have "$c" || user="$user $c"; done
|
|
201
|
+
if [ -n "$user" ]; then emit 3 runtimes userspace DO "install in user space (no sudo):$user"
|
|
202
|
+
else emit 3 runtimes userspace SKIP "bun and uv present"; fi
|
|
203
|
+
|
|
204
|
+
if have mcp; then emit 3 runtimes mcp SKIP "mcp<2> present"; else emit 3 runtimes mcp DO "install mcp<2> via pip"; fi
|
|
205
|
+
if have pi; then emit 3 runtimes pi SKIP "the Pi harness present"; else emit 3 runtimes pi DO "install the Pi harness"; fi
|
|
206
|
+
if have hermes; then emit 3 runtimes hermes SKIP "the Hermes worker runtime present"; else emit 3 runtimes hermes DO "install the Hermes worker runtime"; fi
|
|
207
|
+
|
|
208
|
+
if have herdr; then emit 3 runtimes backend SKIP "herdr — Þjazi (the preferred terminal backend)"
|
|
209
|
+
elif have tmux; then emit 3 runtimes backend SKIP "tmux — the accepted reference backend"
|
|
210
|
+
else emit 3 runtimes backend DO "install herdr (Þjazi), else tmux — without a backend no Eindri can be seated"; fi
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# ── phase 4 · engines — the OSS anvils Ymir wraps ───────────────────────────
|
|
214
|
+
engine_row() { # <bin> <label>
|
|
215
|
+
if have "$1"; then emit 4 engines "$1" SKIP "$2 present"
|
|
216
|
+
else emit 4 engines "$1" DO "install $2 ($1)"; fi
|
|
217
|
+
}
|
|
218
|
+
engines_phase() {
|
|
219
|
+
engine_row treehouse "Yggdrasil (the worktree engine)"
|
|
220
|
+
engine_row no-mistakes "the clean-PR gate (Mjollnir · Glitnir)"
|
|
221
|
+
if have sandcastle; then emit 4 engines sandcastle SKIP "Utgard present"
|
|
222
|
+
else emit 4 engines sandcastle DO "install sandcastle (Utgard) if available"; fi
|
|
223
|
+
|
|
224
|
+
if have docker || have podman; then
|
|
225
|
+
local img
|
|
226
|
+
if have docker && docker image inspect utgard-runner:latest >/dev/null 2>&1; then
|
|
227
|
+
emit 4 engines utgard-image SKIP "utgard-runner:latest present"
|
|
228
|
+
elif have podman && podman image exists utgard-runner:latest >/dev/null 2>&1; then
|
|
229
|
+
emit 4 engines utgard-image SKIP "utgard-runner:latest present (podman)"
|
|
230
|
+
else
|
|
231
|
+
emit 4 engines utgard-image DO "build the sandbox image utgard-runner:latest"
|
|
232
|
+
fi
|
|
233
|
+
else
|
|
234
|
+
emit 4 engines utgard-image BLOCKED "no container engine — the sandbox cannot be built"
|
|
235
|
+
fi
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# ── phase 5 · apps — the surfaces the operator actually sees ────────────────
|
|
239
|
+
# Four surfaces, one shape each: web build required, Electron shell gated.
|
|
240
|
+
# The apps are their own packages (@zerwiz/<app>) — the distro depends on them.
|
|
241
|
+
app_row() { # <surface> <about> [<package>]
|
|
242
|
+
local name="$1" about="$2" pkg="${3:-$1}" dir
|
|
243
|
+
if dir="$(app_dir "$name" "$pkg")"; then
|
|
244
|
+
if [ -d "$dir/dist" ] || [ -d "$dir/out" ]; then
|
|
245
|
+
emit 5 apps "$name" SKIP "$about — installed, and its build shipped with it"
|
|
246
|
+
else
|
|
247
|
+
emit 5 apps "$name" DO "$about — installed from source; the web build is still to make"
|
|
248
|
+
fi
|
|
249
|
+
elif grep -q "\"@zerwiz/$pkg\"" "$ROOT/package.json" 2>/dev/null; then
|
|
250
|
+
# Declared as a dependency of the distro but not present: the package exists,
|
|
251
|
+
# the tree simply has not fetched it. That is a DO, not a dead end.
|
|
252
|
+
emit 5 apps "$name" DO "$about — declared as a dependency but not fetched (npm i -g @zerwiz/ymir fetches it)"
|
|
253
|
+
else
|
|
254
|
+
emit 5 apps "$name" BLOCKED "$about — no apps/$name, no @zerwiz/$pkg package, and the distro does not depend on it"
|
|
255
|
+
fi
|
|
256
|
+
}
|
|
257
|
+
apps_phase() {
|
|
258
|
+
app_row hlidskjalf "the control plane (the high seat), served on :3888"
|
|
259
|
+
app_row odrerir "the live hall (chat + council)"
|
|
260
|
+
app_row sessrumnir "the seat-hall desktop"
|
|
261
|
+
app_row smidja "the smithy and its visualizer (:8437)" smidja-factory
|
|
262
|
+
|
|
263
|
+
# A shell is ready only when its runtime VERIFIES: npm gates install scripts,
|
|
264
|
+
# and a skipped Electron postinstall leaves a partial runtime that still builds
|
|
265
|
+
# the web app and still reports success (bin/electron-lib.sh).
|
|
266
|
+
if [ -z "${YMIR_ELECTRON_LIB_LOADED:-}" ]; then
|
|
267
|
+
for c in "$SCRIPT_DIR/electron-lib.sh"; do [ -r "$c" ] && { . "$c"; YMIR_ELECTRON_LIB_LOADED=1; }; done
|
|
268
|
+
fi
|
|
269
|
+
local shells=0 partial="" missing=0 d
|
|
270
|
+
for d in hlidskjalf odrerir sessrumnir; do
|
|
271
|
+
dir="$(app_dir "$d" 2>/dev/null || true)"
|
|
272
|
+
if [ -z "$dir" ]; then missing=$((missing+1)); continue; fi
|
|
273
|
+
case "$(electron_runtime_state "$dir" 2>/dev/null || true)" in
|
|
274
|
+
ok) shells=$((shells+1)) ;;
|
|
275
|
+
partial) partial="$partial $d" ;;
|
|
276
|
+
*) missing=$((missing+1)) ;;
|
|
277
|
+
esac
|
|
278
|
+
done
|
|
279
|
+
if [ "$shells" -ge 3 ]; then
|
|
280
|
+
emit 5 apps electron SKIP "the three desktop shells' runtimes verify"
|
|
281
|
+
elif [ -n "$partial" ]; then
|
|
282
|
+
emit 5 apps electron DO "the runtime is PARTIAL for:$partial — npm skipped the Electron postinstall; the web surfaces stand, the shells will not launch"
|
|
283
|
+
elif [ "${YMIR_NO_DESKTOP:-0}" = 1 ]; then
|
|
284
|
+
emit 5 apps electron SKIP "declined (--no-desktop) — the web surfaces stand without the shells"
|
|
285
|
+
else
|
|
286
|
+
emit 5 apps electron CONSENT "build the desktop shells? each pulls a ~100 MB Electron runtime — the web surfaces need none of it"
|
|
287
|
+
fi
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# ── phase 6 · wire — the way in, and the agent set ──────────────────────────
|
|
291
|
+
wire_phase() {
|
|
292
|
+
local door="none"
|
|
293
|
+
if [ -x "$ROOT/bin/ymir-setup-auth.sh" ]; then
|
|
294
|
+
door="$(bash "$ROOT/bin/ymir-setup-auth.sh" status 2>/dev/null | sed -n '2p' | cut -d, -f1 | tr -d ' "')"
|
|
295
|
+
fi
|
|
296
|
+
if [ -n "$door" ] && [ "$door" != none ]; then
|
|
297
|
+
emit 6 wire auth SKIP "an operator credential is set ($door)"
|
|
298
|
+
else
|
|
299
|
+
emit 6 wire auth CONSENT "no credential — a password or GitHub sign-in; without it the gate has no door"
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
local codes=""
|
|
303
|
+
[ -x "$ROOT/bin/ymir-invite.sh" ] && codes="$(bash "$ROOT/bin/ymir-invite.sh" list 2>/dev/null | grep -c '^ "' || true)"
|
|
304
|
+
if [ "${codes:-0}" -gt 0 ]; then emit 6 wire invite SKIP "a live invite code exists"
|
|
305
|
+
else emit 6 wire invite CONSENT "mint an invite code — how anyone else is let in (registration stays closed without one)"; fi
|
|
306
|
+
|
|
307
|
+
if [ -d /usr/share/omarchy ]; then
|
|
308
|
+
emit 6 wire launchers DO "place each app on its own numbered desktop + write the launcher entries"
|
|
309
|
+
else
|
|
310
|
+
emit 6 wire launchers SKIP "not an Omarchy host — the Omarchy layer skips cleanly (Rule 05)"
|
|
311
|
+
fi
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
# ── phase 7 · raise — the services, then what the operator sees ─────────────
|
|
315
|
+
raise_phase() {
|
|
316
|
+
local down="" p
|
|
317
|
+
for p in 3888:Hlidskjalf 3889:gate-API 4603:Bifrost 8437:Smidja-visualizer; do
|
|
318
|
+
port_up "${p%%:*}" || down="$down ${p#*:}"
|
|
319
|
+
done
|
|
320
|
+
if [ -n "$down" ]; then emit 7 raise services DO "raise:$down"
|
|
321
|
+
else emit 7 raise services SKIP "every service is listening (SPA · gate API · Bifrost · visualizer)"; fi
|
|
322
|
+
if have hermes; then emit 7 raise well INFO "the memory well (engram) is provisioned by its own step"; fi
|
|
323
|
+
emit 7 raise desktop CONSENT "open the desktop apps so they are seen, not merely installed"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# ── phase 8 · verify — what stands, honestly ────────────────────────────────
|
|
327
|
+
verify_phase() {
|
|
328
|
+
if [ -x "$SCRIPT_DIR/ymir-validate.sh" ]; then
|
|
329
|
+
emit 8 verify validate DO "observe the result: ports, stores, processes, the sandbox image"
|
|
330
|
+
else
|
|
331
|
+
emit 8 verify validate BLOCKED "bin/ymir-validate.sh is absent — the install could not be proven"
|
|
332
|
+
fi
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
resolve_phase; code_phase; home_phase; runtimes_phase; engines_phase; apps_phase; wire_phase; raise_phase; verify_phase
|
|
336
|
+
|
|
337
|
+
# ── output ──────────────────────────────────────────────────────────────────
|
|
338
|
+
if [ "$JSON" = 1 ]; then
|
|
339
|
+
printf '['
|
|
340
|
+
local_i=0
|
|
341
|
+
for i in "${!P_STEP[@]}"; do
|
|
342
|
+
[ -n "$ONLY_PHASE" ] && [ "${P_N[$i]}" != "$ONLY_PHASE" ] && continue
|
|
343
|
+
[ "$ONLY_BLOCKED" = 1 ] && [ "${P_STATE[$i]}" != BLOCKED ] && continue
|
|
344
|
+
[ "$local_i" -gt 0 ] && printf ','
|
|
345
|
+
printf '\n {"phase":%s,"name":"%s","step":"%s","state":"%s","why":"%s"}' \
|
|
346
|
+
"${P_N[$i]}" "${P_NAME[$i]}" "${P_STEP[$i]}" "${P_STATE[$i]}" "${P_WHY[$i]}"
|
|
347
|
+
local_i=$((local_i+1))
|
|
348
|
+
done
|
|
349
|
+
[ "$local_i" -gt 0 ] && printf '\n'
|
|
350
|
+
printf ']\n'
|
|
351
|
+
exit 0
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
shown=0
|
|
355
|
+
for i in "${!P_STEP[@]}"; do
|
|
356
|
+
[ -n "$ONLY_PHASE" ] && [ "${P_N[$i]}" != "$ONLY_PHASE" ] && continue
|
|
357
|
+
[ "$ONLY_BLOCKED" = 1 ] && [ "${P_STATE[$i]}" != BLOCKED ] && continue
|
|
358
|
+
shown=$((shown+1))
|
|
359
|
+
done
|
|
360
|
+
|
|
361
|
+
if [ "$shown" -eq 0 ]; then
|
|
362
|
+
if [ "$ONLY_BLOCKED" = 1 ]; then printf 'plan: 0 blocked steps — nothing is holding the install back\n'
|
|
363
|
+
else printf 'plan: 0 rows for phase %s\n' "$ONLY_PHASE"; fi
|
|
364
|
+
exit 0
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
# A human sees the same rows, rendered in the cloth, on stderr — the TOON below
|
|
368
|
+
# stays the data on stdout, so a pipeline never parses a decoration.
|
|
369
|
+
if [ "$COLOUR" = 1 ]; then
|
|
370
|
+
. "$SCRIPT_DIR/ymir-style.sh"
|
|
371
|
+
style_init
|
|
372
|
+
last_phase=""
|
|
373
|
+
for i in "${!P_STEP[@]}"; do
|
|
374
|
+
[ -n "$ONLY_PHASE" ] && [ "${P_N[$i]}" != "$ONLY_PHASE" ] && continue
|
|
375
|
+
[ "$ONLY_BLOCKED" = 1 ] && [ "${P_STATE[$i]}" != BLOCKED ] && continue
|
|
376
|
+
if [ "${P_N[$i]}" != "$last_phase" ]; then
|
|
377
|
+
style_heading "${P_NAME[$i]}"
|
|
378
|
+
last_phase="${P_N[$i]}"
|
|
379
|
+
fi
|
|
380
|
+
style_line "${P_STATE[$i]}" "${P_STEP[$i]}" "${P_WHY[$i]}"
|
|
381
|
+
done
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
printf 'plan[%d]{phase,name,step,state,why}:\n' "$shown"
|
|
385
|
+
for i in "${!P_STEP[@]}"; do
|
|
386
|
+
[ -n "$ONLY_PHASE" ] && [ "${P_N[$i]}" != "$ONLY_PHASE" ] && continue
|
|
387
|
+
[ "$ONLY_BLOCKED" = 1 ] && [ "${P_STATE[$i]}" != BLOCKED ] && continue
|
|
388
|
+
printf ' "%s","%s","%s","%s","%s"\n' "${P_N[$i]}" "${P_NAME[$i]}" "${P_STEP[$i]}" "${P_STATE[$i]}" "${P_WHY[$i]}"
|
|
389
|
+
done
|
|
390
|
+
exit 0
|
package/bin/ymir-say.sh
CHANGED
|
@@ -24,7 +24,20 @@ set -u
|
|
|
24
24
|
VERSION="1.0.0"
|
|
25
25
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
26
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
# The roots that live OUTSIDE the code tree: this machine's records and the
|
|
29
|
+
# runtime state belong to the home the operator chose at installation, never in
|
|
30
|
+
# the tree — a packaged install replaces its tree on upgrade (Rule 04).
|
|
31
|
+
if [ -z "${YMIR_HOARD_LIB_LOADED:-}" ]; then
|
|
32
|
+
_yr="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
|
+
for _yc in "$_yr/hoard-lib.sh" "$(dirname "$_yr")/bin/hoard-lib.sh"; do
|
|
34
|
+
[ -r "$_yc" ] && { . "$_yc"; YMIR_HOARD_LIB_LOADED=1; break; }
|
|
35
|
+
done
|
|
36
|
+
unset _yr _yc
|
|
37
|
+
fi
|
|
38
|
+
hoard_state_dir YMIR_STATE_DIR
|
|
39
|
+
hoard_data_dir YMIR_DATA_DIR
|
|
40
|
+
STATE_DIR="${BROKK_STATE_OVERRIDE:-$YMIR_STATE_DIR}"
|
|
28
41
|
LOG="$STATE_DIR/ymir-said.log"
|
|
29
42
|
|
|
30
43
|
case "${1-}" in
|
package/bin/ymir-setup-auth.sh
CHANGED
|
@@ -18,7 +18,18 @@ set -u
|
|
|
18
18
|
VERSION="1.0.0"
|
|
19
19
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
20
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
# The operator's credentials are theirs and live in the HOME they chose — never
|
|
23
|
+
# in the code tree, which a packaged install replaces on upgrade (Rule 04).
|
|
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_local_env _YMIR_ENV_DEFAULT
|
|
32
|
+
ENV_FILE="${YMIR_ENV_FILE:-$_YMIR_ENV_DEFAULT}"
|
|
22
33
|
|
|
23
34
|
usage() { sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; }
|
|
24
35
|
die() { printf 'error: %s\n' "$1" >&2; exit 1; }
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ymir-style.sh — the cloth of the terminal halls.
|
|
3
|
+
#
|
|
4
|
+
# Ymir had no design for its own command line: correct output, no experience.
|
|
5
|
+
# This is the cloth — cut from the same stone as the halls
|
|
6
|
+
# (midgard/design-system/tokens.css): bone for words, bronze for what acts,
|
|
7
|
+
# steel for what stands, blood for what is wrong, and the forge's marks for the
|
|
8
|
+
# states a plan can be in.
|
|
9
|
+
#
|
|
10
|
+
# The rules it obeys (cli-guidelines, clig.dev, Monospace TUI):
|
|
11
|
+
# · colour, marks and motion only when a human is present — never in a pipe,
|
|
12
|
+
# never when NO_COLOR is set, never on a dumb terminal, never for --no-color
|
|
13
|
+
# · data goes to stdout, the human's rendering goes to stderr, so a pipeline
|
|
14
|
+
# never has to parse a decoration
|
|
15
|
+
# · a reaction for every action, and a next step for every ending
|
|
16
|
+
# · density first: no banner bigger than four lines, no rule longer than the text
|
|
17
|
+
#
|
|
18
|
+
# Source-safe; functions only. Nothing here writes a byte of data.
|
|
19
|
+
set -u
|
|
20
|
+
|
|
21
|
+
# --- the palette, from the tokens (truecolor → 256 → none) ------------------
|
|
22
|
+
style_init() {
|
|
23
|
+
STYLE_ON=0
|
|
24
|
+
# A human must be present: stderr is a terminal and nobody said no.
|
|
25
|
+
if [ -t 2 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != dumb ] && [ "${YMIR_NO_COLOR:-0}" != 1 ]; then
|
|
26
|
+
STYLE_ON=1
|
|
27
|
+
fi
|
|
28
|
+
if [ "$STYLE_ON" = 1 ] && { [ "${COLORTERM:-}" = truecolor ] || [ "${COLORTERM:-}" = 24bit ]; }; then
|
|
29
|
+
C_BONE=$'\033[38;2;207;195;169m'; C_BRONZE=$'\033[38;2;201;151;79m'
|
|
30
|
+
C_STEEL=$'\033[38;2;150;160;168m'; C_BLOOD=$'\033[38;2;194;88;74m'
|
|
31
|
+
C_FAINT=$'\033[38;2;107;98;80m'; C_BOLD=$'\033[1m'
|
|
32
|
+
elif [ "$STYLE_ON" = 1 ]; then
|
|
33
|
+
C_BONE=$'\033[38;5;187m'; C_BRONZE=$'\033[38;5;179m'
|
|
34
|
+
C_STEEL=$'\033[38;5;109m'; C_BLOOD=$'\033[38;5;167m'
|
|
35
|
+
C_FAINT=$'\033[38;5;101m'; C_BOLD=$'\033[1m'
|
|
36
|
+
else
|
|
37
|
+
C_BONE=""; C_BRONZE=""; C_STEEL=""; C_BLOOD=""; C_FAINT=""; C_BOLD=""
|
|
38
|
+
fi
|
|
39
|
+
C_OFF=""
|
|
40
|
+
[ "$STYLE_ON" = 1 ] && C_OFF=$'\033[0m'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# The forge's marks, one per state a thing can be in.
|
|
44
|
+
style_mark() { # <state> → a glyph
|
|
45
|
+
case "${1:-}" in
|
|
46
|
+
DO|do) printf '%s' '◆' ;;
|
|
47
|
+
SKIP|skip|already) printf '%s' '·' ;;
|
|
48
|
+
INFO|info) printf '%s' '—' ;;
|
|
49
|
+
BLOCKED|blocked) printf '%s' '✕' ;;
|
|
50
|
+
CONSENT|consent) printf '%s' '?' ;;
|
|
51
|
+
OK|ok) printf '%s' '✓' ;;
|
|
52
|
+
WARN|warn) printf '%s' '·' ;;
|
|
53
|
+
FAIL|fail) printf '%s' '✕' ;;
|
|
54
|
+
*) printf '%s' '·' ;;
|
|
55
|
+
esac
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
style_colour() { # <state> → the colour for it
|
|
59
|
+
case "${1:-}" in
|
|
60
|
+
DO|CONSENT|WARN|warn|do|consent) printf '%s' "$C_BRONZE" ;;
|
|
61
|
+
OK|SKIP|INFO|ok|skip|info) printf '%s' "$C_STEEL" ;;
|
|
62
|
+
FAIL|BLOCKED|fail|blocked) printf '%s' "$C_BLOOD" ;;
|
|
63
|
+
*) printf '%s' "$C_FAINT" ;;
|
|
64
|
+
esac
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# --- the pieces -------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
# The mark: four lines at most. The WORDS show even in a pipe — only the colour
|
|
70
|
+
# is conditional, because colour is decoration and the name is information.
|
|
71
|
+
style_title() { # <name> <claim>
|
|
72
|
+
printf '%s\n' "${C_BRONZE} ᛉ ${C_BOLD}${1}${C_OFF}" >&2
|
|
73
|
+
printf '%s\n' "${C_FAINT} ${2}${C_OFF}" >&2
|
|
74
|
+
printf '\n' >&2
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
style_rule() { # a hairline, no wider than the words it separates
|
|
78
|
+
local width="${1:-52}"
|
|
79
|
+
[ "$STYLE_ON" = 1 ] || return 0
|
|
80
|
+
local line=""; local i=0
|
|
81
|
+
while [ "$i" -lt "$width" ]; do line="$line─"; i=$((i+1)); done
|
|
82
|
+
printf '%s\n' "${C_FAINT}${line}${C_OFF}" >&2
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
style_say() { printf '%s\n' "${C_BONE}$*${C_OFF}" >&2; }
|
|
86
|
+
style_hint() { printf '%s\n' "${C_FAINT}$*${C_OFF}" >&2; }
|
|
87
|
+
|
|
88
|
+
style_line() { # <state> <name> <detail> — the one line per thing
|
|
89
|
+
local state="$1" name="$2" detail="${3:-}"
|
|
90
|
+
printf ' %s%s%s %-12s %s%s\n' \
|
|
91
|
+
"$(style_colour "$state")" "$(style_mark "$state")" "$C_OFF" \
|
|
92
|
+
"$name" "$(style_colour "$state")" "$detail" >&2
|
|
93
|
+
printf '%s' "$C_OFF" >/dev/null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
style_heading() { printf '\n%s%s%s\n' "$C_BRONZE" "$1" "$C_OFF" >&2; }
|
|
97
|
+
|
|
98
|
+
# The ending: what stands, then exactly what to type next. Every CLI deserves
|
|
99
|
+
# to leave the operator with the next step and nothing else to guess.
|
|
100
|
+
style_next() { # one command per line, as "verb — what it does"
|
|
101
|
+
printf '\n%s\n' "${C_BOLD}Where to go from here${C_OFF}" >&2
|
|
102
|
+
while [ $# -gt 0 ]; do
|
|
103
|
+
local cmd="${1%% — *}" what="${1#* — }"
|
|
104
|
+
printf ' %s%-22s%s %s\n' "$C_BRONZE" "$cmd" "$C_OFF" "$(printf '%s' "$what" | sed "s/^/$C_FAINT/;s/$/$C_OFF/")" >&2
|
|
105
|
+
shift
|
|
106
|
+
done
|
|
107
|
+
printf '\n' >&2
|
|
108
|
+
}
|
package/bin/ymir-validate.sh
CHANGED
|
@@ -16,6 +16,24 @@ set -u
|
|
|
16
16
|
VERSION="1.0.0"
|
|
17
17
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
18
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
19
|
+
# The cloth: colour and marks for the human reading this report; the TOON rows on
|
|
20
|
+
# stdout stay the data (bin/ymir-style.sh).
|
|
21
|
+
if [ -z "${YMIR_STYLE_LOADED:-}" ]; then
|
|
22
|
+
. "$SCRIPT_DIR/ymir-style.sh"; YMIR_STYLE_LOADED=1
|
|
23
|
+
fi
|
|
24
|
+
style_init
|
|
25
|
+
# Where the smithy's parts live: apps/smidja-factory in a clone, or the
|
|
26
|
+
# @zerwiz/smidja-factory package in an npm install (bin/smidja-lib.sh).
|
|
27
|
+
if [ -z "${YMIR_SMIDJA_LIB_LOADED:-}" ]; then
|
|
28
|
+
_ys="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
|
+
for _yc in "$_ys/smidja-lib.sh" "$(dirname "$_ys")/bin/smidja-lib.sh"; do
|
|
30
|
+
[ -r "$_yc" ] && { . "$_yc"; YMIR_SMIDJA_LIB_LOADED=1; break; }
|
|
31
|
+
done
|
|
32
|
+
unset _ys _yc
|
|
33
|
+
fi
|
|
34
|
+
smidja_visualizer_dir SMIDJA_VIZ
|
|
35
|
+
smidja_factory_dir SMIDJA_FACTORY
|
|
36
|
+
|
|
19
37
|
# shellcheck source=bin/ymir-platform.sh
|
|
20
38
|
. "$SCRIPT_DIR/ymir-platform.sh"
|
|
21
39
|
# Docker or rootless Podman (Fedora), whichever is present.
|
|
@@ -99,21 +117,35 @@ else
|
|
|
99
117
|
fi
|
|
100
118
|
|
|
101
119
|
# ── 7. smidja db (visualizer readiness) ─────────────────────────────────────
|
|
120
|
+
# The same pair scripts/start.sh and bin/smidja-bootstrap.sh resolve: an existing
|
|
121
|
+
# $YMIR_HOME/smidja/smidja.db first, then an in-repo copy.
|
|
102
122
|
YMIR_HOME="${YMIR_HOME:-$HOME/Documents/Ymir}"
|
|
103
|
-
SMIDJA_DB_PATH="${
|
|
104
|
-
|
|
123
|
+
SMIDJA_DB_PATH="${SMIDJA_DB:-}"
|
|
124
|
+
if [ -z "$SMIDJA_DB_PATH" ]; then
|
|
125
|
+
for c in "$YMIR_HOME/smidja/smidja.db" "$ROOT/apps/smidja/smidja_data/smidja.db"; do
|
|
126
|
+
[ -f "$c" ] && { SMIDJA_DB_PATH="$c"; break; }
|
|
127
|
+
done
|
|
128
|
+
SMIDJA_DB_PATH="${SMIDJA_DB_PATH:-$YMIR_HOME/smidja/smidja.db}"
|
|
129
|
+
fi
|
|
105
130
|
if [ -f "$SMIDJA_DB_PATH" ]; then
|
|
106
131
|
add smidja-db PASS "smidja.db present"
|
|
107
132
|
else
|
|
108
133
|
add smidja-db FAIL "smidja.db missing — run bin/smidja-bootstrap.sh"
|
|
109
134
|
fi
|
|
110
135
|
|
|
111
|
-
# ── 7b. visualizer
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
136
|
+
# ── 7b. visualizer (built ./dist AND its API actually listening) ────────────
|
|
137
|
+
# A PASS must mean the thing is UP. Probe the port, not just the build: a
|
|
138
|
+
# built-but-dead visualizer (a bad CMD_DB, a crashed API) is a FAIL, not green.
|
|
139
|
+
VIZ="${SMIDJA_VIZ:-}"
|
|
140
|
+
VIZ_PORT="${SMIDJA_VIZ_API_PORT:-8437}"
|
|
141
|
+
if [ ! -d "$VIZ" ]; then
|
|
142
|
+
add visualizer SKIP "no visualizer tree at $VIZ"
|
|
143
|
+
elif [ ! -d "$VIZ/dist" ]; then
|
|
116
144
|
add visualizer FAIL "UI not built (./dist missing) — (cd $VIZ && bun run build)"
|
|
145
|
+
elif port_open "$VIZ_PORT"; then
|
|
146
|
+
add visualizer PASS "UI built and served on :$VIZ_PORT"
|
|
147
|
+
else
|
|
148
|
+
add visualizer FAIL "UI built but no API listening on :$VIZ_PORT — run scripts/start.sh"
|
|
117
149
|
fi
|
|
118
150
|
|
|
119
151
|
# ── 8. desktop apps (both Electron windows) ─────────────────────────────────
|
|
@@ -133,8 +165,14 @@ if port_open 4602; then add memory PASS "Mimir bridge up on :4602"
|
|
|
133
165
|
else add memory WARN "well off (:4602) — needs the engram engine; platform runs without it"; fi
|
|
134
166
|
|
|
135
167
|
# ── 10. audit ledger intact ─────────────────────────────────────────────────
|
|
136
|
-
RUNES_LEDGER="
|
|
137
|
-
|
|
168
|
+
RUNES_LEDGER=""
|
|
169
|
+
# The ledger follows the hoard (Rule 06): 0004-hoard-and-realms moved it under
|
|
170
|
+
# hodd/memory/. Try the hoard first, then the pre-move locations.
|
|
171
|
+
for c in "${YMIR_HOME:+${YMIR_HOARD:-$YMIR_HOME/hodd}/memory/runes_audit.md}" \
|
|
172
|
+
"${YMIR_HOME:+$YMIR_HOME/memory/runes_audit.md}" \
|
|
173
|
+
"$ROOT/workspace/memory/runes_audit.md"; do
|
|
174
|
+
[ -n "$c" ] && [ -r "$c" ] && { RUNES_LEDGER="$c"; break; }
|
|
175
|
+
done
|
|
138
176
|
if [ -r "$RUNES_LEDGER" ]; then
|
|
139
177
|
add runes PASS "audit ledger readable ($(wc -l <"$RUNES_LEDGER" | tr -d ' ') lines)"
|
|
140
178
|
else
|
|
@@ -162,12 +200,27 @@ else
|
|
|
162
200
|
for i in "${!IDS[@]}"; do
|
|
163
201
|
say " \"${IDS[$i]}\",\"${STATES[$i]}\",\"${DETAILS[$i]}\""
|
|
164
202
|
done
|
|
203
|
+
# The same rows, rendered for the eye, on stderr: a human reads marks and
|
|
204
|
+
# colour, a pipeline reads the TOON above, and neither parses the other.
|
|
205
|
+
if [ "$QUIET" != 1 ]; then
|
|
206
|
+
printf '\n' >&2
|
|
207
|
+
for i in "${!IDS[@]}"; do
|
|
208
|
+
style_line "${STATES[$i]}" "${IDS[$i]}" "${DETAILS[$i]}"
|
|
209
|
+
done
|
|
210
|
+
fi
|
|
165
211
|
fi
|
|
166
212
|
|
|
167
213
|
if [ "$fails" -gt 0 ]; then
|
|
168
|
-
[ "$QUIET" = 1 ] ||
|
|
214
|
+
[ "$QUIET" = 1 ] || {
|
|
215
|
+
style_rule 52
|
|
216
|
+
style_line FAIL "not usable" "$fails required check(s) failed, $warns warning(s)"
|
|
217
|
+
style_hint "mend it: ymir eir (diagnose every surface, then mend what is broken)"
|
|
218
|
+
}
|
|
169
219
|
printf 'help: %s required check(s) failed, %s warning(s)\n' "$fails" "$warns" >&2
|
|
170
220
|
exit 1
|
|
171
221
|
fi
|
|
172
|
-
[ "$QUIET" = 1 ] ||
|
|
222
|
+
[ "$QUIET" = 1 ] || {
|
|
223
|
+
style_rule 52
|
|
224
|
+
style_line OK "stands" "every required check passes ($warns warning(s))"
|
|
225
|
+
}
|
|
173
226
|
exit 0
|