@zerwiz/ymir 0.1.8 → 0.1.10
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/skills/galdr-ymirsystem/assets/installation.md +93 -0
- package/.agents/skills/groa-update/SKILL.md +9 -0
- package/.agents/skills/ymir-host/assets/install.md +29 -0
- package/CHANGELOG.md +96 -0
- package/README.md +54 -0
- package/bin/app-lib.sh +58 -0
- package/bin/design-check.sh +12 -1
- package/bin/design-icon.sh +15 -0
- package/bin/desktop-place.sh +34 -6
- package/bin/eir-doctor.sh +12 -1
- package/bin/hall-snapshot.sh +12 -1
- package/bin/sessrumnir-ensure.sh +12 -1
- package/bin/sessrumnir-sync.sh +12 -1
- package/bin/sessrumnir.sh +12 -1
- package/bin/smidja-lib.sh +14 -13
- package/bin/ymir-install.sh +14 -3
- package/bin/ymir-invite.sh +14 -3
- package/bin/ymir-style.sh +13 -0
- package/bin/ymir.js +20 -0
- package/package.json +1 -1
- package/scripts/electron.sh +15 -2
- package/scripts/start.sh +34 -8
|
@@ -192,6 +192,99 @@ cloth_rules[4]{rule,why}:
|
|
|
192
192
|
TOON stays on stdout. The installer prints it at the consent, and ends with the
|
|
193
193
|
next steps — *a reaction for every action, a next step for every ending*.
|
|
194
194
|
|
|
195
|
+
### A clone and a package are one tree — `bin/app-lib.sh`
|
|
196
|
+
|
|
197
|
+
The apps are the only thing that differs between the two shapes, and only in
|
|
198
|
+
where they live:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
surface a clone an npm install
|
|
202
|
+
--------------- ---------------------- ------------------------------------
|
|
203
|
+
hlidskjalf apps/hlidskjalf node_modules/@zerwiz/hlidskjalf
|
|
204
|
+
hlidskjalf-mobile apps/hlidskjalf-mobile node_modules/@zerwiz/hlidskjalf-mobile
|
|
205
|
+
odrerir apps/odrerir node_modules/@zerwiz/odrerir
|
|
206
|
+
sessrumnir apps/sessrumnir node_modules/@zerwiz/sessrumnir
|
|
207
|
+
smidja apps/smidja-factory node_modules/@zerwiz/smidja-factory
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`bin/app-lib.sh` answers for both — `app_dir <surface> <var>`, `app_pkg
|
|
211
|
+
<surface>` — and **eighteen files** were converted to it: the raise path
|
|
212
|
+
(`scripts/start.sh`), the windows (`scripts/electron.sh`), the invite door, the
|
|
213
|
+
seat-hall trio, Eir, the icon mint, the desktop placement, the hall snapshot, and
|
|
214
|
+
the installer's own SPA and shell steps. `bin/smidja-lib.sh` delegates to it, so
|
|
215
|
+
there is **one** truth about where things live.
|
|
216
|
+
|
|
217
|
+
**The trap that named itself.** `printf -v <name>` writes to the *function's*
|
|
218
|
+
scope. A helper whose scratch variable shares the caller's requested name
|
|
219
|
+
swallows the answer — `app_dir hlidskjalf c` returned nothing because the
|
|
220
|
+
helper's own `local c` held the path. Every scratch name in the resolvers is
|
|
221
|
+
function-prefixed (`_apd_c`, `_smd_c`, `_apr_root`) for exactly that reason, and a
|
|
222
|
+
new resolver must follow the rule.
|
|
223
|
+
|
|
224
|
+
**Why it matters beyond tidiness:** a packaged install shipped with eighteen
|
|
225
|
+
scripts looking in `apps/`, so `ymir raise` died on
|
|
226
|
+
`cd …/apps/hlidskjalf: No such file or directory` while every package had in fact
|
|
227
|
+
arrived. A shape assumption is a bug that only shows itself on the *other* shape.
|
|
228
|
+
|
|
229
|
+
### The hall answers on the port it was given
|
|
230
|
+
|
|
231
|
+
`scripts/start.sh` raises the SPA, and the SPA must listen on
|
|
232
|
+
`$HLIDSKJALF_PORT` (3888 by default) — every other door looks for it there. Two
|
|
233
|
+
rules came out of a packaged install where it listened nowhere:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
spa_serving[2]{shape,how}:
|
|
237
|
+
"a packaged install","serves its built ./dist with `vite preview --port $PORT --strictPort`"
|
|
238
|
+
"a clone","serves the dev server, but STILL on $PORT — `npm run dev -- --port $PORT --strictPort`"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
A bare `npm run dev` lets Vite take 5173 and the hall silently answers on the
|
|
242
|
+
wrong port; the raise now prints the log's last lines when the port stays silent
|
|
243
|
+
rather than claiming success. Óðrerir resolves through `bin/app-lib.sh` like every
|
|
244
|
+
other surface (it read as `missing apps/odrerir` on a packaged install).
|
|
245
|
+
|
|
246
|
+
### The marks land on any desktop — not only Omarchy's
|
|
247
|
+
|
|
248
|
+
`bin/desktop-place.sh` holds **two kinds of thing**, and they were behind one gate:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
desktop_halves[2]{half,who_reads_it}:
|
|
252
|
+
"the window rules (numbered desktops, the Lua rule)","Hyprland / Omarchy only"
|
|
253
|
+
"the launcher entries and the rune icons","the freedesktop standard — GNOME, KDE, Hyprland alike"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
A GNOME operator gets entries and icons and no window rules, which is the correct
|
|
257
|
+
answer — and the reason a packaged install on GNOME placed *nothing* while the
|
|
258
|
+
clone's marks already existed. `bin/desktop-place.sh entries` is the launcher half
|
|
259
|
+
alone (any Linux desktop), the installer's `marks` step calls it, and both the
|
|
260
|
+
desktop database and the icon cache are refreshed after.
|
|
261
|
+
|
|
262
|
+
### What a user went from, and to
|
|
263
|
+
|
|
264
|
+
`npm install -g` prints *"changed 266 packages"* and no versions. The CLI records
|
|
265
|
+
the version it last ran in the home's state (`state/version`) and says the
|
|
266
|
+
transition **once**, on the first run after an update:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
the tree moved 0.1.7 → 0.1.9
|
|
270
|
+
run `ymir eir` to see what stands, `ymir raise` to lift the hall
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### The patience words — `style_patience`
|
|
274
|
+
|
|
275
|
+
A long hour must say what it is doing. `bin/ymir-style.sh` carries the line, and
|
|
276
|
+
the installer's long chain and the raise path's build open with it:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
◆ much moves the halls are being stood up for the first time
|
|
280
|
+
this hour is long, and nothing of yours is lost in it —
|
|
281
|
+
roots come home, shapes are re-cut, names are set true again.
|
|
282
|
+
Your patience is noted, and it is earned.
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Written in the house voice, kept under four lines (density), and shown on stderr
|
|
286
|
+
with everything else the cloth renders.
|
|
287
|
+
|
|
195
288
|
### Two libs the packaged tree needs
|
|
196
289
|
|
|
197
290
|
```
|
|
@@ -62,6 +62,15 @@ This touches only the Brokk repo and its own worktrees, never anything under `pr
|
|
|
62
62
|
Summarize what landed without internal vocabulary: which parts of the fleet are now on the latest, and which were left as-is and why.
|
|
63
63
|
Surface any skipped target whose reason needs the Allfather's attention (a home with un-landed changes, local edits).
|
|
64
64
|
|
|
65
|
+
5. **Re-check where the apps live — an update can move the shape.**
|
|
66
|
+
`bin/app-lib.sh` resolves a surface in a clone (`apps/<surface>`) or in a
|
|
67
|
+
package (`node_modules/@zerwiz/<package>`); `bin/smidja-lib.sh` and
|
|
68
|
+
`bin/electron-lib.sh` do the same for the smithy and a shell's runtime. After a
|
|
69
|
+
fast-forward, run `bin/ymir-plan.sh --phase 5` — if a surface that stood
|
|
70
|
+
installed now reads `BLOCKED`, the layout assumption moved and the resolver
|
|
71
|
+
needs the new shape. A long update should also say so: `style_patience` from
|
|
72
|
+
`bin/ymir-style.sh` opens the slow part in the house's own voice.
|
|
73
|
+
|
|
65
74
|
5. **Read the plan after an update — the new version may expect more of this host.**
|
|
66
75
|
A tracked change can add a step, a root, or a setting the running machine has not
|
|
67
76
|
met yet. After a fast-forward that moved the instruction surface, ask what the
|
|
@@ -48,6 +48,35 @@ To move an existing home to a new place, re-run the install with the new value:
|
|
|
48
48
|
YMIR_HOME=/path/to/new-home bin/ymir-install.sh
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## The doors — what the operator types afterwards
|
|
52
|
+
|
|
53
|
+
Two commands land on PATH (`ymir`, `ymir-install`); everything else is a door on
|
|
54
|
+
`ymir`, named for the figure who does the work. `ymir --help` lists them all.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
ymir raise | lower lift the hall, or lay it down
|
|
58
|
+
ymir hlidskjalf | sessrumnir the two windows
|
|
59
|
+
ymir smidja the board on :8437 — build · start · stop · status
|
|
60
|
+
ymir heimdall [invite] the way in, and letting someone else in
|
|
61
|
+
ymir eir what stands, and mend what does not
|
|
62
|
+
ymir groa [migrate] take the latest, and mend this home forward
|
|
63
|
+
ymir mimir · sense · plan
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Both shapes — a clone and an npm install
|
|
67
|
+
|
|
68
|
+
The operator may have cloned the tree or installed the package; the scripts must
|
|
69
|
+
not care. `bin/app-lib.sh` resolves a surface either way (`apps/<surface>`, else
|
|
70
|
+
`node_modules/@zerwiz/<package>`), `bin/smidja-lib.sh` resolves the smithy, and
|
|
71
|
+
`bin/electron-lib.sh` verifies a shell's runtime. To exercise the *other* shape
|
|
72
|
+
without a second machine:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
YMIR_ROOT_DIR=/path/to/an/npm/@zerwiz/ymir bash bin/app-lib.sh # resolve against it
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
That variable is how a resolver is tested against a package from a clone.
|
|
79
|
+
|
|
51
80
|
## Which root holds what
|
|
52
81
|
|
|
53
82
|
```
|
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,100 @@
|
|
|
1
1
|
|
|
2
|
+
## 2026-09-17 — 0.1.10: the hall answers on its port, and the marks land
|
|
3
|
+
|
|
4
|
+
- **The SPA listens where it is asked.** A bare `npm run dev` let Vite take 5173,
|
|
5
|
+
so `:3888` answered nothing in either shape; a packaged tree now serves its built
|
|
6
|
+
`./dist` and every shape passes `--port "$HLIDSKJALF_PORT" --strictPort`.
|
|
7
|
+
- **Óðrerir resolves** through `bin/app-lib.sh` instead of the clone's `apps/`.
|
|
8
|
+
- **The launcher entries and rune icons land on any Linux desktop** — the
|
|
9
|
+
freedesktop half of desktop integration is no longer behind Omarchy's gate, so a
|
|
10
|
+
GNOME operator gets marks at all.
|
|
11
|
+
- **The version a user went from and to** is said once, on the first run after an
|
|
12
|
+
update: *the tree moved 0.1.9 → 0.1.10*.
|
|
13
|
+
|
|
14
|
+
## 2026-09-17 — npm-only: the hall answers on its own port, and the marks land on any desktop
|
|
15
|
+
|
|
16
|
+
The Allfather turned off every service running from the clone and asked for the
|
|
17
|
+
npm path alone to be true.
|
|
18
|
+
|
|
19
|
+
- **The SPA never listened on `:3888`.** `scripts/start.sh` raised it with a bare
|
|
20
|
+
`npm run dev`, so Vite took 5173 — then 5174 — while `ymir-validate.sh` and
|
|
21
|
+
every door looked for `:3888`. The port is now our decision
|
|
22
|
+
(`--port "$HLIDSKJALF_PORT" --strictPort`), a packaged install serves its built
|
|
23
|
+
`./dist` through `vite preview` instead of a dev server, and the raise prints
|
|
24
|
+
the last lines of the log when the port does not answer instead of claiming
|
|
25
|
+
success.
|
|
26
|
+
- **Óðrerir was skipped on a packaged install** — `HALL_DIR="$ROOT/apps/odrerir"`,
|
|
27
|
+
the clone's layout again. It resolves through `bin/app-lib.sh` now
|
|
28
|
+
(`app_dir odrerir`), like every other surface.
|
|
29
|
+
- **The launcher entries and the icons are the freedesktop half, not Omarchy's.**
|
|
30
|
+
Both halves of `bin/desktop-place.sh` sat behind the Omarchy gate, so a GNOME
|
|
31
|
+
operator got *nothing* — no entries, no rune icons — which is why the desktop
|
|
32
|
+
marks from the clone existed and the ones from npm never appeared. `entries` is
|
|
33
|
+
its own verb now (any Linux desktop), the installer's `marks` step calls it, and
|
|
34
|
+
the desktop database and icon cache are refreshed afterwards.
|
|
35
|
+
- **A user can see what moved.** `npm install -g` says *"changed 266 packages"* and
|
|
36
|
+
no versions. The CLI records the version it last ran and says the transition
|
|
37
|
+
once: *the tree moved 0.1.7 → 0.1.9 · run `ymir eir` to see what stands*.
|
|
38
|
+
|
|
39
|
+
## 2026-09-17 — the doors, written down where a user will find them
|
|
40
|
+
|
|
41
|
+
- **`README.md` gains "After it installs — the doors"**: the two commands on PATH,
|
|
42
|
+
the twelve doors on `ymir`, the lawless name that answers once and points at the
|
|
43
|
+
lawful one, the patience words in full, and a section on **both shapes** — a
|
|
44
|
+
clone and a package, with one resolver and one rule (colour for the eye, TOON
|
|
45
|
+
for the pipe).
|
|
46
|
+
- **The skills that must know are taught.** Galdr's `assets/installation.md` gains
|
|
47
|
+
the shape table, the eighteen converted call sites, and the trap that named
|
|
48
|
+
itself (`printf -v` writes to the function's scope — scratch names are
|
|
49
|
+
function-prefixed for that reason). The `ymir-host` skill's `assets/install.md`
|
|
50
|
+
gains the doors and how to exercise the *other* shape without a second machine
|
|
51
|
+
(`YMIR_ROOT_DIR`). The `groa-update` skill gains the step that matters after
|
|
52
|
+
every update: **re-check where the apps live** — a shape assumption is a bug
|
|
53
|
+
that only shows itself on the other shape — and say the long hour in the house's
|
|
54
|
+
voice.
|
|
55
|
+
|
|
56
|
+
## 2026-09-17 — 0.1.9: both shapes reach the registry
|
|
57
|
+
|
|
58
|
+
- **`ymir raise` works on a packaged install.** 0.1.8 shipped before the resolver,
|
|
59
|
+
so a user who installed from npm hit `cd …/apps/hlidskjalf: No such file or
|
|
60
|
+
directory` — every package had arrived, and eighteen scripts were looking in the
|
|
61
|
+
clone's `apps/`. `bin/app-lib.sh` resolves a surface in either shape now.
|
|
62
|
+
- **A clone and a package are one tree to the scripts.** Proven against both: the
|
|
63
|
+
five surfaces resolve from `/home/zerwiz/Ymir` and from an npm-installed
|
|
64
|
+
package, and `ymir raise` against a packaged tree fetches the app's
|
|
65
|
+
dependencies, raises the gate API, starts Nornir cron, and brings up Bifrost and
|
|
66
|
+
the well.
|
|
67
|
+
- **The patience words** — `style_patience` — open the installer's long chain and
|
|
68
|
+
the raise path's build, so a long hour says what it is doing.
|
|
69
|
+
|
|
70
|
+
## 2026-09-17 — both shapes: a clone and an npm install, and the patience words
|
|
71
|
+
|
|
72
|
+
- **`ymir raise` failed on a packaged install** — and the reason was ours, not
|
|
73
|
+
npm's. Every package had arrived; the scripts looked for the apps in the clone's
|
|
74
|
+
`apps/`, where a package keeps nothing. `scripts/start.sh` died on
|
|
75
|
+
`cd …/apps/hlidskjalf: No such file or directory`. **Eighteen files assumed the
|
|
76
|
+
clone's layout.**
|
|
77
|
+
- **One resolver, both shapes:** `bin/app-lib.sh` — `apps/<surface>` in a clone,
|
|
78
|
+
`node_modules/@zerwiz/<package>` in a package, with the surface→package map
|
|
79
|
+
(`smidja` → `@zerwiz/smidja-factory`). Wired into the raise path, the windows,
|
|
80
|
+
the invite door, the seat-hall, Eir, the icons, the placement, the hall snapshot
|
|
81
|
+
and the installer's own SPA and shell steps. `bin/smidja-lib.sh` delegates to it
|
|
82
|
+
now, so there is one truth about where things live.
|
|
83
|
+
- **Proven in both trees**: all five surfaces resolve from `/home/zerwiz/Ymir`
|
|
84
|
+
(a clone) and from an npm-installed package. And `ymir raise` run against the
|
|
85
|
+
packaged tree no longer dies — it fetches the app's dependencies, raises the
|
|
86
|
+
gate API, **starts Nornir cron**, and brings up Bifrost and the well.
|
|
87
|
+
- **A trap worth naming:** `printf -v <name>` writes to the *function's* scope, so
|
|
88
|
+
a helper whose scratch variable shares the caller's requested name swallows the
|
|
89
|
+
answer. Every scratch name in the resolvers is function-prefixed for that
|
|
90
|
+
reason.
|
|
91
|
+
- **The patience words.** A long install should say what it is doing and why it
|
|
92
|
+
takes a while. `style_patience` — the cloth's own line — now opens the
|
|
93
|
+
installer's long chain and the raise path's build:
|
|
94
|
+
*much moves · the halls are being set right · this hour is long, and nothing of
|
|
95
|
+
yours is lost in it — roots come home, shapes are re-cut, names are set true
|
|
96
|
+
again. Your patience is noted, and it is earned.*
|
|
97
|
+
|
|
2
98
|
## 2026-09-17 — 0.1.8: the day's work reaches npm
|
|
3
99
|
|
|
4
100
|
- **The doors.** `ymir` now carries the operator's verbs, each named for the
|
package/README.md
CHANGED
|
@@ -666,3 +666,57 @@ packages (Hlidskjalf, Óðrerir, Sessrúmnir, Smíðja) — one per app.
|
|
|
666
666
|
Private data never touches npm — all user data lives at `$YMIR_HOME` (the hoard
|
|
667
667
|
at `hodd/` and the realms at `svartalfaheim/`) and syncs via the user's
|
|
668
668
|
**private** GitHub repo, never a public one.
|
|
669
|
+
|
|
670
|
+
### After it installs — the doors
|
|
671
|
+
|
|
672
|
+
`npm install -g @zerwiz/ymir` puts **two** commands on your PATH: `ymir` and
|
|
673
|
+
`ymir-install`. Everything else is a door on `ymir`, named for the figure whose
|
|
674
|
+
work it does — because a great many doors either open or they do not, and a name
|
|
675
|
+
that says whose hand is on it is the difference between guessing and knowing.
|
|
676
|
+
|
|
677
|
+
```
|
|
678
|
+
ymir first setup: the plan, then your word
|
|
679
|
+
ymir raise | lower lift the hall, or lay it down
|
|
680
|
+
ymir hlidskjalf the high seat's window
|
|
681
|
+
ymir smidja the smithy's board (:8437) — build · start · stop · status
|
|
682
|
+
ymir sessrumnir the seat-hall's window
|
|
683
|
+
ymir heimdall the way in — your credential (status · set · github)
|
|
684
|
+
ymir invite let someone else in — mint · list · revoke
|
|
685
|
+
ymir eir what stands, and mend what does not
|
|
686
|
+
ymir groa [migrate] take the latest, and mend this home forward
|
|
687
|
+
ymir mimir · sense · plan
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
A name the law has not given a home still answers, once, and tells you the name
|
|
691
|
+
that has it: `ymir doctor` replies *the door is named `ymir eir` now*.
|
|
692
|
+
|
|
693
|
+
**A long hour says so, in its own voice.** The installer opens with the plan and,
|
|
694
|
+
when the work will take a while, tells you plainly:
|
|
695
|
+
|
|
696
|
+
```
|
|
697
|
+
◆ much moves the halls are being stood up for the first time
|
|
698
|
+
this hour is long, and nothing of yours is lost in it —
|
|
699
|
+
roots come home, shapes are re-cut, names are set true again.
|
|
700
|
+
Your patience is noted, and it is earned.
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Both shapes — a clone and a package are one tree
|
|
704
|
+
|
|
705
|
+
Ymir runs the same whether you **cloned** it or **installed** it from npm. The
|
|
706
|
+
apps are the difference, and only in where they live:
|
|
707
|
+
|
|
708
|
+
| | a clone | an npm install |
|
|
709
|
+
|---|---|---|
|
|
710
|
+
| the surfaces | `apps/hlidskjalf` · `odrerir` · `sessrumnir` · `smidja-factory` | `node_modules/@zerwiz/hlidskjalf` · `odrerir` · `sessrumnir` · `smidja-factory` |
|
|
711
|
+
| how they got there | the registry's `repo: apps/<path>` block clones each | declared as dependencies of the distro |
|
|
712
|
+
|
|
713
|
+
One resolver (`bin/app-lib.sh`) answers for both, so a script never has to care
|
|
714
|
+
which shape it woke in — and the same is true of the smithy (`bin/smidja-lib.sh`)
|
|
715
|
+
and of a desktop shell's runtime (`bin/electron-lib.sh`). The plan tells you which
|
|
716
|
+
surfaces are present, which are declared but not yet fetched, and which have no
|
|
717
|
+
package at all.
|
|
718
|
+
|
|
719
|
+
**Colour is for the eye, never for the pipe.** `bin/ymir-style.sh` renders in the
|
|
720
|
+
halls' own cloth — bone for words, bronze for what acts, steel for what stands,
|
|
721
|
+
blood for what is wrong — and only when a human is watching (a terminal, no
|
|
722
|
+
`NO_COLOR`). What a pipeline reads on stdout stays plain TOON.
|
package/bin/app-lib.sh
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# app-lib.sh — where an app actually lives.
|
|
3
|
+
#
|
|
4
|
+
# Two installs, one name. In a clone the apps sit at `apps/<surface>` (the
|
|
5
|
+
# registry's `repo: apps/<path>` block clones them there). In a packaged install
|
|
6
|
+
# the tree has no `apps/` at all: each surface arrives as a dependency, at
|
|
7
|
+
# `node_modules/@zerwiz/<package>`.
|
|
8
|
+
#
|
|
9
|
+
# surface package
|
|
10
|
+
# ---------------- ---------------------
|
|
11
|
+
# hlidskjalf hlidskjalf
|
|
12
|
+
# hlidskjalf-mobile hlidskjalf-mobile
|
|
13
|
+
# odrerir odrerir
|
|
14
|
+
# sessrumnir sessrumnir
|
|
15
|
+
# smidja smidja-factory
|
|
16
|
+
#
|
|
17
|
+
# A surface's name is not always its package's name — and a path is not a name at
|
|
18
|
+
# all. Every script that needs an app resolves it HERE, so the two shapes can
|
|
19
|
+
# never disagree (Rule 07). Source-safe; functions only.
|
|
20
|
+
#
|
|
21
|
+
# app_dir <surface> <result-var> the app's directory (empty + exit 1 if absent)
|
|
22
|
+
# app_pkg <surface> the npm package name for that surface
|
|
23
|
+
set -u
|
|
24
|
+
|
|
25
|
+
app_pkg() { # <surface> → the package name npm serves
|
|
26
|
+
case "${1-}" in
|
|
27
|
+
smidja) printf '%s' 'smidja-factory' ;;
|
|
28
|
+
*) printf '%s' "${1-}" ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
app_dir() { # <surface> <result-var> — a clone's apps/<x>, else the package
|
|
33
|
+
# Scratch names are function-prefixed on purpose: `printf -v <name>` writes to THIS
|
|
34
|
+
# function's scope, so a scratch variable sharing the caller's requested name
|
|
35
|
+
# would swallow the answer (a caller asking for "c" or "root" would get nothing).
|
|
36
|
+
local _apd_surface="${1-}" _apd_rv="${2-}" _apd_root _apd_pkg _apd_c
|
|
37
|
+
[ -n "$_apd_surface" ] && [ -n "$_apd_rv" ] || return 2
|
|
38
|
+
_apd_root="${YMIR_ROOT_DIR:-}"
|
|
39
|
+
if [ -z "$_apd_root" ]; then _apd_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; fi
|
|
40
|
+
_apd_pkg="$(app_pkg "$_apd_surface")"
|
|
41
|
+
for _apd_c in "$_apd_root/apps/$_apd_surface" "$_apd_root/node_modules/@zerwiz/$_apd_pkg"; do
|
|
42
|
+
[ -d "$_apd_c" ] && { printf -v "$_apd_rv" '%s' "$_apd_c"; return 0; }
|
|
43
|
+
done
|
|
44
|
+
printf -v "$_apd_rv" '%s' ""
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Some callers need the directory a mint writes INTO (an app's public/, electron/,
|
|
49
|
+
# dist/…). A packaged app is read-only in spirit, so the clone's apps/ is preferred
|
|
50
|
+
# when it exists; otherwise the package is the only truth there is.
|
|
51
|
+
app_root() { # <result-var> — where apps live in THIS tree
|
|
52
|
+
local _apr_rv="${1-}" _apr_root
|
|
53
|
+
[ -n "$_apr_rv" ] || return 2
|
|
54
|
+
_apr_root="${YMIR_ROOT_DIR:-}"
|
|
55
|
+
if [ -z "$_apr_root" ]; then _apr_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; fi
|
|
56
|
+
if [ -d "$_apr_root/apps" ]; then printf -v "$_apr_rv" '%s' "$_apr_root/apps"; return 0; fi
|
|
57
|
+
printf -v "$_apr_rv" '%s' "$_apr_root/node_modules/@zerwiz"
|
|
58
|
+
}
|
package/bin/design-check.sh
CHANGED
|
@@ -19,8 +19,19 @@ set -u
|
|
|
19
19
|
VERSION="1.0.0"
|
|
20
20
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
21
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
22
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
23
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
24
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
25
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
27
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
28
|
+
done
|
|
29
|
+
unset _ya _yac
|
|
30
|
+
fi
|
|
31
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
32
|
+
|
|
22
33
|
TOKENS="$ROOT/midgard/design-system/tokens.css"
|
|
23
|
-
SEEDS="$
|
|
34
|
+
SEEDS="$APP_SESSRUMNIR/src/renderer/src/themes/fensalir.json"
|
|
24
35
|
|
|
25
36
|
case "${1-}" in
|
|
26
37
|
-v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;;
|
package/bin/design-icon.sh
CHANGED
|
@@ -16,6 +16,21 @@ 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
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
20
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
21
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
22
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
24
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
25
|
+
done
|
|
26
|
+
unset _ya _yac
|
|
27
|
+
fi
|
|
28
|
+
app_dir hlidskjalf APP_HLIDSKJALF || APP_HLIDSKJALF=""
|
|
29
|
+
app_dir hlidskjalf-mobile APP_HLIDSKJALF_MOBILE || APP_HLIDSKJALF_MOBILE=""
|
|
30
|
+
app_dir odrerir APP_ODRERIR || APP_ODRERIR=""
|
|
31
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
32
|
+
app_dir smidja-factory APP_SMIDJA_FACTORY || APP_SMIDJA_FACTORY=""
|
|
33
|
+
|
|
19
34
|
ICONS="$ROOT/midgard/design-system/icons"
|
|
20
35
|
STONE="#0e0c09"
|
|
21
36
|
|
package/bin/desktop-place.sh
CHANGED
|
@@ -29,6 +29,19 @@ fi
|
|
|
29
29
|
VERSION="1.0.0"
|
|
30
30
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
31
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
32
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
33
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
34
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
35
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
36
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
37
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
38
|
+
done
|
|
39
|
+
unset _ya _yac
|
|
40
|
+
fi
|
|
41
|
+
app_dir hlidskjalf APP_HLIDSKJALF || APP_HLIDSKJALF=""
|
|
42
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
43
|
+
app_dir odrerir APP_ODRERIR || APP_ODRERIR=""
|
|
44
|
+
|
|
32
45
|
HYPR_DIR="$HOME/.config/hypr"
|
|
33
46
|
RULE_FILE="$HYPR_DIR/ymir-desktops.lua"
|
|
34
47
|
# The three Ymir surfaces, in the order they should claim desktops.
|
|
@@ -38,15 +51,20 @@ CLASS_smidja="ymir-smidja"
|
|
|
38
51
|
CLASS_sessrumnir="sessrumnir"
|
|
39
52
|
CLASS_odrerir="ymir-odrerir"
|
|
40
53
|
|
|
41
|
-
# ── launcher entries (
|
|
54
|
+
# ── launcher entries (ANY Linux desktop — GNOME, KDE, Hyprland) ──────────────
|
|
55
|
+
# Two halves live in this file, and they are not the same kind of thing:
|
|
56
|
+
# · the WINDOW RULES are Hyprland's (Omarchy) — numbered desktops, a Lua rule;
|
|
57
|
+
# · the LAUNCHER ENTRIES and the app icons are the freedesktop standard, which
|
|
58
|
+
# GNOME, KDE and Hyprland all read from the same place.
|
|
59
|
+
# A GNOME operator gets entries and icons and no window rules, which is the
|
|
60
|
+
# correct answer — and the reason a packaged install on GNOME had no icons at
|
|
61
|
+
# all, because both halves lived behind the Omarchy gate.
|
|
62
|
+
#
|
|
42
63
|
# The .desktop files are templates (__YMIR_ROOT__, not an absolute path), because
|
|
43
|
-
# where this checkout lives is a fact about the machine, not about Ymir.
|
|
44
|
-
# rendered into the user's applications directory. This is DESKTOP INTEGRATION —
|
|
45
|
-
# Omarchy/Hyprland is the supported environment; another OS needs its own launcher,
|
|
46
|
-
# not a branch of this one.
|
|
64
|
+
# where this checkout lives is a fact about the machine, not about Ymir.
|
|
47
65
|
install_entries() {
|
|
48
66
|
local dst="$HOME/.local/share/applications" n=0
|
|
49
|
-
local dirs=("$
|
|
67
|
+
local dirs=("$APP_HLIDSKJALF/electron" "$APP_SESSRUMNIR/resources" "$APP_ODRERIR/electron")
|
|
50
68
|
if [ "$(ymir_os)" != linux ] && [ "$(ymir_os)" != wsl ]; then
|
|
51
69
|
printf 'skip: desktop entries are Omarchy/Linux-shaped; this host is %s\n' "$(ymir_os)" >&2
|
|
52
70
|
return 0
|
|
@@ -182,6 +200,16 @@ include_launchers_file() {
|
|
|
182
200
|
}
|
|
183
201
|
|
|
184
202
|
case "$ACTION" in
|
|
203
|
+
entries)
|
|
204
|
+
# The launcher half on its own: any Linux desktop, no Hyprland required.
|
|
205
|
+
install_entries
|
|
206
|
+
if command -v update-desktop-database >/dev/null 2>&1; then
|
|
207
|
+
update-desktop-database "$HOME/.local/share/applications" >/dev/null 2>&1 || true
|
|
208
|
+
fi
|
|
209
|
+
if command -v gtk-update-icon-cache >/dev/null 2>&1 && [ -d "$HOME/.local/share/icons/hicolor" ]; then
|
|
210
|
+
gtk-update-icon-cache -q -t -f "$HOME/.local/share/icons/hicolor" >/dev/null 2>&1 || true
|
|
211
|
+
fi
|
|
212
|
+
;;
|
|
185
213
|
plan)
|
|
186
214
|
plan="$(plan_desktops)"
|
|
187
215
|
printf 'desktop-place[%d]{app,desktop}:\n' "$(printf '%s\n' "$plan" | grep -c .)"
|
package/bin/eir-doctor.sh
CHANGED
|
@@ -22,6 +22,17 @@ STATE="${BROKK_STATE_OVERRIDE:-$YMIR_HOME/state}"
|
|
|
22
22
|
# stdout stay the data (bin/ymir-style.sh).
|
|
23
23
|
if [ -z "${YMIR_STYLE_LOADED:-}" ]; then . "$SCRIPT_DIR/ymir-style.sh"; YMIR_STYLE_LOADED=1; fi
|
|
24
24
|
style_init
|
|
25
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
26
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
27
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
28
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
30
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
31
|
+
done
|
|
32
|
+
unset _ya _yac
|
|
33
|
+
fi
|
|
34
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
35
|
+
|
|
25
36
|
|
|
26
37
|
case "${1-}" in -v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;; -h|--help|"") sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; esac
|
|
27
38
|
ACTION="${1:-check}"; shift || true
|
|
@@ -154,7 +165,7 @@ detail() { # <name> -> one short fact
|
|
|
154
165
|
herdr) "have herdr && herdr --version 2>/dev/null | head -1 || echo 'herdr absent'" ;;
|
|
155
166
|
a2abridge) "have a2abridge && a2abridge --version 2>/dev/null | head -1 || echo 'engine absent'" ;;
|
|
156
167
|
hermes) "have hermes && echo present || echo absent" ;;
|
|
157
|
-
sessrumnir)"[ -d $
|
|
168
|
+
sessrumnir)"[ -d $APP_SESSRUMNIR/out ] && echo built || echo 'not built'" ;;
|
|
158
169
|
well) "echo 'engram :4602'" ;;
|
|
159
170
|
mcp) "echo 'a2abridge + engram'" ;;
|
|
160
171
|
lock) "cat $STATE/.lock 2>/dev/null | tr -d '[:space:]' | sed 's/^/pid /' || echo none" ;;
|
package/bin/hall-snapshot.sh
CHANGED
|
@@ -16,6 +16,17 @@
|
|
|
16
16
|
# (the Live Hall's own deck; the merged public site takes it from its build).
|
|
17
17
|
set -u
|
|
18
18
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
20
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
21
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
22
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
24
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
25
|
+
done
|
|
26
|
+
unset _ya _yac
|
|
27
|
+
fi
|
|
28
|
+
app_dir odrerir APP_ODRERIR || APP_ODRERIR=""
|
|
29
|
+
|
|
19
30
|
ROOT="${BROKK_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
20
31
|
|
|
21
32
|
# The operator's settings and secrets live in the home they chose, never in the
|
|
@@ -37,7 +48,7 @@ hoard_data_dir YMIR_DATA_DIR
|
|
|
37
48
|
# runtime state belong to the home the operator chose at installation, never in
|
|
38
49
|
# the tree — a packaged install replaces its tree on upgrade (Rule 04).
|
|
39
50
|
STATE="${BROKK_STATE_OVERRIDE:-$YMIR_STATE_DIR}"
|
|
40
|
-
OUT="${1:-$
|
|
51
|
+
OUT="${1:-$APP_ODRERIR/public/livehall.json}"
|
|
41
52
|
|
|
42
53
|
runes_file="${BROKK_RUNES_FILE:-${YMIR_HOME:-$HOME/Documents/Ymir}/hodd/memory/runes_audit.md}"
|
|
43
54
|
PROJECTS_FILE="${YMIR_HOME:-$HOME/Documents/Ymir}/hodd/identity/projects.yaml"
|
package/bin/sessrumnir-ensure.sh
CHANGED
|
@@ -20,7 +20,18 @@ set -u
|
|
|
20
20
|
VERSION="1.0.0"
|
|
21
21
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
22
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
23
|
-
|
|
23
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
24
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
25
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
26
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
28
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
29
|
+
done
|
|
30
|
+
unset _ya _yac
|
|
31
|
+
fi
|
|
32
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
33
|
+
|
|
34
|
+
APP="$APP_SESSRUMNIR"
|
|
24
35
|
|
|
25
36
|
case "${1-}" in -v|-V|--version) printf '%s\n' "$VERSION"; exit 0 ;; -h|--help|"") sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; esac
|
|
26
37
|
CMD="${1-}"; shift || true
|
package/bin/sessrumnir-sync.sh
CHANGED
|
@@ -24,7 +24,18 @@
|
|
|
24
24
|
set -euo pipefail
|
|
25
25
|
|
|
26
26
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
27
|
-
|
|
27
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
28
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
29
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
30
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
32
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
33
|
+
done
|
|
34
|
+
unset _ya _yac
|
|
35
|
+
fi
|
|
36
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
37
|
+
|
|
38
|
+
APP="$APP_SESSRUMNIR"
|
|
28
39
|
FORK="$APP/fork"
|
|
29
40
|
UPSTREAM_REPO="${SESSRUMNIR_UPSTREAM:-https://github.com/zerwiz/pi-desktop.git}"
|
|
30
41
|
OWNED_JSON="$FORK/owned.json"
|
package/bin/sessrumnir.sh
CHANGED
|
@@ -14,6 +14,17 @@ set -u
|
|
|
14
14
|
VERSION="1.0.0"
|
|
15
15
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
16
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
17
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
18
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
19
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
20
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
22
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
23
|
+
done
|
|
24
|
+
unset _ya _yac
|
|
25
|
+
fi
|
|
26
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
27
|
+
|
|
17
28
|
|
|
18
29
|
# The roots that live OUTSIDE the code tree: this machine's records and the
|
|
19
30
|
# runtime state belong to the home the operator chose at installation, never in
|
|
@@ -27,7 +38,7 @@ if [ -z "${YMIR_HOARD_LIB_LOADED:-}" ]; then
|
|
|
27
38
|
fi
|
|
28
39
|
hoard_state_dir YMIR_STATE_DIR
|
|
29
40
|
hoard_data_dir YMIR_DATA_DIR
|
|
30
|
-
APP="$
|
|
41
|
+
APP="$APP_SESSRUMNIR"
|
|
31
42
|
PID_FILE="$YMIR_STATE_DIR/sessrumnir.pid"
|
|
32
43
|
LOG_FILE="$YMIR_STATE_DIR/sessrumnir.log"
|
|
33
44
|
|
package/bin/smidja-lib.sh
CHANGED
|
@@ -20,20 +20,21 @@ SMIDJA_SURFACE="smidja"
|
|
|
20
20
|
SMIDJA_PACKAGE="smidja-factory"
|
|
21
21
|
|
|
22
22
|
smidja_factory_dir() { # <result-var> — the smithy's directory, or empty
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
# The general resolver (bin/app-lib.sh) knows both shapes; the smithy is one
|
|
24
|
+
# surface among them. The old skill symlink is the last resort, and a dangling
|
|
25
|
+
# one is not a home — the visualizer must be readable there.
|
|
26
|
+
local _smd_rv=${1-} _smd_c
|
|
27
|
+
[ -n "$_smd_rv" ] || return 2
|
|
28
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
29
|
+
local _sl; _sl="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
30
|
+
[ -r "$_sl/app-lib.sh" ] && { . "$_sl/app-lib.sh"; YMIR_APP_LIB_LOADED=1; }
|
|
27
31
|
fi
|
|
28
|
-
|
|
29
|
-
"$
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
[ -d "$c/apps/visualizer" ] && { printf -v "$result_var" '%s' "$c"; return 0; }
|
|
35
|
-
done
|
|
36
|
-
printf -v "$result_var" '%s' ""
|
|
32
|
+
if app_dir smidja _smd_c 2>/dev/null && [ -d "$_smd_c/apps/visualizer" ]; then
|
|
33
|
+
printf -v "$_smd_rv" '%s' "$_smd_c"; return 0
|
|
34
|
+
fi
|
|
35
|
+
_smd_c="${YMIR_ROOT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/.agents/skills/$SMIDJA_PACKAGE"
|
|
36
|
+
[ -d "$_smd_c/apps/visualizer" ] && { printf -v "$_smd_rv" '%s' "$_smd_c"; return 0; }
|
|
37
|
+
printf -v "$_smd_rv" '%s' ""
|
|
37
38
|
return 1
|
|
38
39
|
}
|
|
39
40
|
|
package/bin/ymir-install.sh
CHANGED
|
@@ -53,6 +53,8 @@ hoard_local_env YMIR_ENV_FILE
|
|
|
53
53
|
# repo (Rule 04).
|
|
54
54
|
# shellcheck source=bin/hoard-lib.sh
|
|
55
55
|
. "$SCRIPT_DIR/hoard-lib.sh"
|
|
56
|
+
# shellcheck source=bin/app-lib.sh
|
|
57
|
+
. "$SCRIPT_DIR/app-lib.sh"
|
|
56
58
|
ymir_home_root YMIR_HOME
|
|
57
59
|
# Where the smithy's parts live: apps/smidja-factory in a clone, or the
|
|
58
60
|
# @zerwiz/smidja-factory package in an npm install (bin/smidja-lib.sh).
|
|
@@ -585,7 +587,8 @@ step_smidja() {
|
|
|
585
587
|
# Hlidskjalf installs with npm (its own lockfile), not bun, and its API serves the
|
|
586
588
|
# UI from ./dist. Without this step a fresh clone has no SPA and no build.
|
|
587
589
|
step_spa() {
|
|
588
|
-
local app="
|
|
590
|
+
local app; app_dir hlidskjalf app || app=""
|
|
591
|
+
local app_ok="$app"
|
|
589
592
|
[ -d "$app" ] || { add hlidskjalf SKIP "no apps/hlidskjalf"; return 0; }
|
|
590
593
|
if [ "$CHECK" = 0 ]; then
|
|
591
594
|
if ! have npm; then
|
|
@@ -669,6 +672,13 @@ step_marks() {
|
|
|
669
672
|
fi
|
|
670
673
|
"$SCRIPT_DIR/design-icon.sh" mint --all >/dev/null 2>&1 || true
|
|
671
674
|
n=$("$SCRIPT_DIR/design-icon.sh" install 2>/dev/null | grep -c '"ymir-') || n=0
|
|
675
|
+
# The launcher entries are the freedesktop half of desktop integration, and they
|
|
676
|
+
# belong to ANY Linux desktop — not only to Omarchy's window rules. A GNOME
|
|
677
|
+
# operator gets entries and icons and no window rules; before this, both halves
|
|
678
|
+
# sat behind the Omarchy gate and a packaged install placed nothing at all.
|
|
679
|
+
if [ -x "$SCRIPT_DIR/desktop-place.sh" ]; then
|
|
680
|
+
"$SCRIPT_DIR/desktop-place.sh" entries >/dev/null 2>&1 || true
|
|
681
|
+
fi
|
|
672
682
|
[ -r "$HOME/.pi/agent/AGENTS.md" ] || [ -d "$HOME/.pi/agent" ] && {
|
|
673
683
|
ln -sfn "$ROOT/AGENTS.md" "$HOME/.pi/agent/AGENTS.md" 2>/dev/null || true
|
|
674
684
|
}
|
|
@@ -708,8 +718,8 @@ step_desktop() {
|
|
|
708
718
|
fi
|
|
709
719
|
local shell partial=""
|
|
710
720
|
for shell in hlidskjalf odrerir sessrumnir; do
|
|
711
|
-
local dir
|
|
712
|
-
|
|
721
|
+
local dir d
|
|
722
|
+
app_dir "$shell" dir || continue
|
|
713
723
|
d="$(electron_runtime_state "$dir" 2>/dev/null || true)"
|
|
714
724
|
[ "$d" = partial ] && partial="$partial $shell"
|
|
715
725
|
done
|
|
@@ -819,6 +829,7 @@ step_panes() {
|
|
|
819
829
|
|
|
820
830
|
# Ask before touching the machine; --check only previews and never asks.
|
|
821
831
|
[ "$CHECK" = 0 ] && confirm_install
|
|
832
|
+
[ "$CHECK" = 0 ] && style_patience "the halls are being stood up for the first time"
|
|
822
833
|
|
|
823
834
|
step_panes; step_prereqs; step_home; step_tree; step_apps; step_engines; step_models; step_hermes; step_sessrumnir; step_backend; step_host; step_sandbox; step_memory; step_smidja; step_spa; step_omarchy; step_loaders; step_gates; step_marks
|
|
824
835
|
# Migrations MOVE private data — that is a write, and `--check` promises none.
|
package/bin/ymir-invite.sh
CHANGED
|
@@ -16,8 +16,19 @@
|
|
|
16
16
|
set -euo pipefail
|
|
17
17
|
|
|
18
18
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
20
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
21
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
22
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
24
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
25
|
+
done
|
|
26
|
+
unset _ya _yac
|
|
27
|
+
fi
|
|
28
|
+
app_dir hlidskjalf APP_HLIDSKJALF || APP_HLIDSKJALF=""
|
|
29
|
+
|
|
30
|
+
MODULE="$APP_HLIDSKJALF/server/accounts.ts"
|
|
31
|
+
STORE="$APP_HLIDSKJALF/server/accounts.ts" # absolute: one definition of an account, wherever this is run from
|
|
21
32
|
|
|
22
33
|
die() { printf 'ymir-invite: %s\n' "$1" >&2; exit 1; }
|
|
23
34
|
|
|
@@ -25,7 +36,7 @@ command -v bun >/dev/null 2>&1 || die "bun is required (bin/ymir-install.sh inst
|
|
|
25
36
|
|
|
26
37
|
# A tiny bun program per action — the module is the single source of truth.
|
|
27
38
|
accounts() {
|
|
28
|
-
( cd "$
|
|
39
|
+
( cd "$APP_HLIDSKJALF" && bun -e "$1" )
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
[ -f "$MODULE" ] || die "the account store module is missing: $MODULE"
|
package/bin/ymir-style.sh
CHANGED
|
@@ -95,6 +95,19 @@ style_line() { # <state> <name> <detail> — the one line per thing
|
|
|
95
95
|
|
|
96
96
|
style_heading() { printf '\n%s%s%s\n' "$C_BRONZE" "$1" "$C_OFF" >&2; }
|
|
97
97
|
|
|
98
|
+
# For a long hour: say what is happening and why it takes a while. A user who
|
|
99
|
+
# knows the halls are being set right waits; a user watching a silent cursor
|
|
100
|
+
# wonders whether it has broken.
|
|
101
|
+
style_patience() { # [what is being set right]
|
|
102
|
+
local what="${1:-the halls are being set right}"
|
|
103
|
+
printf '\n' >&2
|
|
104
|
+
style_line DO "much moves" "$what"
|
|
105
|
+
style_hint " this hour is long, and nothing of yours is lost in it —"
|
|
106
|
+
style_hint " roots come home, shapes are re-cut, names are set true again."
|
|
107
|
+
style_hint " Your patience is noted, and it is earned."
|
|
108
|
+
printf '\n' >&2
|
|
109
|
+
}
|
|
110
|
+
|
|
98
111
|
# The ending: what stands, then exactly what to type next. Every CLI deserves
|
|
99
112
|
# to leave the operator with the next step and nothing else to guess.
|
|
100
113
|
style_next() { # one command per line, as "verb — what it does"
|
package/bin/ymir.js
CHANGED
|
@@ -49,6 +49,25 @@ const faint = wrap('38;2;107;98;80');
|
|
|
49
49
|
const blood = wrap('38;2;194;88;74');
|
|
50
50
|
const bold = wrap('1');
|
|
51
51
|
|
|
52
|
+
// ── what a user went from, and to ────────────────────────────────────────────
|
|
53
|
+
// `npm install -g` prints "changed 266 packages" and no versions, so an operator
|
|
54
|
+
// cannot see what moved. We can: record the version we last ran, and say the
|
|
55
|
+
// transition once, on the first run after an update.
|
|
56
|
+
const STATE_DIR = process.env.YMIR_STATE_DIR || path.join(process.env.YMIR_HOME || path.join(require('node:os').homedir(), 'Documents', 'Ymir'), 'state');
|
|
57
|
+
function versionNotice() {
|
|
58
|
+
try {
|
|
59
|
+
const f = path.join(STATE_DIR, 'version');
|
|
60
|
+
const seen = fs.existsSync(f) ? fs.readFileSync(f, 'utf8').trim() : '';
|
|
61
|
+
if (seen === pkg.version) return;
|
|
62
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
63
|
+
fs.writeFileSync(f, pkg.version + '\n');
|
|
64
|
+
if (!seen) return; // a first install has nothing to compare
|
|
65
|
+
process.stderr.write(
|
|
66
|
+
bone(` the tree moved `) + faint(`${seen} → `) + bronze(`${pkg.version}`) + '\n' +
|
|
67
|
+
faint(` run \`ymir eir\` to see what stands, \`ymir raise\` to lift the hall\n\n`));
|
|
68
|
+
} catch { /* a version notice must never break a door */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
// ── the doors ────────────────────────────────────────────────────────────────
|
|
53
72
|
// verb → { script, args } — args are prepended to whatever the operator passes,
|
|
54
73
|
// so a verb can be a doorway to a sub-verb of a script that has several.
|
|
@@ -108,6 +127,7 @@ function run(script, args) {
|
|
|
108
127
|
const argv = process.argv.slice(2);
|
|
109
128
|
const first = argv[0];
|
|
110
129
|
|
|
130
|
+
versionNotice();
|
|
111
131
|
if (!first) run(DOORS.install.script, []);
|
|
112
132
|
if (first === '--version' || first === '-v' || first === '-V') {
|
|
113
133
|
process.stdout.write(`${pkg.version}\n`);
|
package/package.json
CHANGED
package/scripts/electron.sh
CHANGED
|
@@ -26,6 +26,19 @@ fi
|
|
|
26
26
|
VERSION="1.1.0"
|
|
27
27
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
28
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
29
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
30
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
31
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
32
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
34
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
35
|
+
done
|
|
36
|
+
unset _ya _yac
|
|
37
|
+
fi
|
|
38
|
+
app_dir hlidskjalf APP_HLIDSKJALF || APP_HLIDSKJALF=""
|
|
39
|
+
app_dir odrerir APP_ODRERIR || APP_ODRERIR=""
|
|
40
|
+
app_dir sessrumnir APP_SESSRUMNIR || APP_SESSRUMNIR=""
|
|
41
|
+
|
|
29
42
|
|
|
30
43
|
# The roots that live OUTSIDE the code tree: this machine's records and the
|
|
31
44
|
# runtime state belong to the home the operator chose at installation, never in
|
|
@@ -39,8 +52,8 @@ if [ -z "${YMIR_HOARD_LIB_LOADED:-}" ]; then
|
|
|
39
52
|
fi
|
|
40
53
|
hoard_state_dir YMIR_STATE_DIR
|
|
41
54
|
hoard_data_dir YMIR_DATA_DIR
|
|
42
|
-
APP="$
|
|
43
|
-
Odrerir_app="$
|
|
55
|
+
APP="$APP_HLIDSKJALF"
|
|
56
|
+
Odrerir_app="$APP_ODRERIR"
|
|
44
57
|
NO_INSTALL=0
|
|
45
58
|
VIEW="${YMIR_DESKTOP_VIEW:-hlidskjalf}"
|
|
46
59
|
VIEWS=(hlidskjalf smidja odrerir)
|
package/scripts/start.sh
CHANGED
|
@@ -17,6 +17,22 @@ if [ -z "${YMIR_PLATFORM_LOADED:-}" ]; then
|
|
|
17
17
|
fi
|
|
18
18
|
|
|
19
19
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
20
|
+
# The cloth (bin/ymir-style.sh) — colour and words for the human watching.
|
|
21
|
+
if [ -z "${YMIR_STYLE_LOADED:-}" ] && [ -r "$ROOT/bin/ymir-style.sh" ]; then
|
|
22
|
+
. "$ROOT/bin/ymir-style.sh"; YMIR_STYLE_LOADED=1; style_init
|
|
23
|
+
fi
|
|
24
|
+
# Where an app lives: apps/<surface> in a clone, node_modules/@zerwiz/<pkg> in an
|
|
25
|
+
# npm install — both shapes, one resolver (bin/app-lib.sh).
|
|
26
|
+
if [ -z "${YMIR_APP_LIB_LOADED:-}" ]; then
|
|
27
|
+
_ya="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
for _yac in "$_ya/app-lib.sh" "$(dirname "$_ya")/bin/app-lib.sh"; do
|
|
29
|
+
[ -r "$_yac" ] && { . "$_yac"; YMIR_APP_LIB_LOADED=1; break; }
|
|
30
|
+
done
|
|
31
|
+
unset _ya _yac
|
|
32
|
+
fi
|
|
33
|
+
app_dir hlidskjalf APP_HLIDSKJALF || APP_HLIDSKJALF=""
|
|
34
|
+
app_dir odrerir HALL_DIR || HALL_DIR=""
|
|
35
|
+
|
|
20
36
|
# Where the smithy's parts live: apps/smidja-factory in a clone, or the
|
|
21
37
|
# @zerwiz/smidja-factory package in an npm install (bin/smidja-lib.sh).
|
|
22
38
|
if [ -z "${YMIR_SMIDJA_LIB_LOADED:-}" ]; then
|
|
@@ -48,7 +64,7 @@ hoard_data_dir YMIR_DATA_DIR
|
|
|
48
64
|
# The roots that live OUTSIDE the code tree: this machine's records and the
|
|
49
65
|
# runtime state belong to the home the operator chose at installation, never in
|
|
50
66
|
# the tree — a packaged install replaces its tree on upgrade (Rule 04).
|
|
51
|
-
APP="$
|
|
67
|
+
APP="$APP_HLIDSKJALF"
|
|
52
68
|
RUN="$ROOT/.run"
|
|
53
69
|
PID_FILE="$RUN/hlidskjalf.pid"
|
|
54
70
|
LOG="$RUN/hlidskjalf.log"
|
|
@@ -80,7 +96,8 @@ else
|
|
|
80
96
|
fi
|
|
81
97
|
|
|
82
98
|
if [[ ! -d "$APP/node_modules" ]]; then
|
|
83
|
-
|
|
99
|
+
style_patience "the seat's own dependencies are being fetched, then the hall is raised"
|
|
100
|
+
echo "Installing dependencies…"
|
|
84
101
|
(cd "$APP" && npm install --no-audit --no-fund)
|
|
85
102
|
fi
|
|
86
103
|
|
|
@@ -180,7 +197,7 @@ fi
|
|
|
180
197
|
|
|
181
198
|
# Óðrerir — the Live Hall. Its own Astro board on :4322, the carved planning
|
|
182
199
|
# glass every hall door opens. Raised before the SPA so a window never waits.
|
|
183
|
-
HALL_DIR="
|
|
200
|
+
app_dir odrerir HALL_DIR || HALL_DIR=""
|
|
184
201
|
HALL_PORT="${ODRERIR_PORT:-4322}"
|
|
185
202
|
HALL_PID_FILE="$RUN/odrerir.pid"
|
|
186
203
|
HALL_LOG="$RUN/odrerir.log"
|
|
@@ -195,13 +212,14 @@ if [ -d "$HALL_DIR" ]; then
|
|
|
195
212
|
echo "Óðrerir — Live Hall raised (pid $(cat "$HALL_PID_FILE")) → http://127.0.0.1:${HALL_PORT}/"
|
|
196
213
|
fi
|
|
197
214
|
else
|
|
198
|
-
echo "Óðrerir — Live Hall skipped (
|
|
215
|
+
echo "Óðrerir — Live Hall skipped (the odrerir surface is not present)." >&2
|
|
199
216
|
fi
|
|
200
217
|
|
|
201
218
|
cd "$APP"
|
|
202
219
|
|
|
203
220
|
if [[ "${1:-}" == "--foreground" ]]; then
|
|
204
|
-
exec
|
|
221
|
+
if [ -d "$APP/dist" ]; then exec npx --no-install vite preview --port "$PORT" --strictPort --host
|
|
222
|
+
else exec npm run dev -- --port "$PORT" --strictPort --host; fi
|
|
205
223
|
fi
|
|
206
224
|
|
|
207
225
|
# The SPA is already serving — nothing more for us to raise here.
|
|
@@ -211,11 +229,18 @@ if [[ "$SPA_UP" == "1" ]]; then
|
|
|
211
229
|
fi
|
|
212
230
|
|
|
213
231
|
# New session so we can signal the whole process group on stop.
|
|
214
|
-
|
|
232
|
+
# The port is OUR decision, not Vite's: a bare `npm run dev` takes 5173 and the
|
|
233
|
+
# hall never answers on the port every other door expects. A packaged install
|
|
234
|
+
# ships ./dist, so it is served; a clone gets the dev server — both on $PORT.
|
|
235
|
+
if [ -d "$APP/dist" ]; then
|
|
236
|
+
ymir_detach bash -c "cd '$APP' && exec npx --no-install vite preview --port '$PORT' --strictPort --host" >"$LOG" 2>&1
|
|
237
|
+
else
|
|
238
|
+
ymir_detach bash -c "cd '$APP' && exec npm run dev -- --port '$PORT' --strictPort --host" >"$LOG" 2>&1
|
|
239
|
+
fi
|
|
215
240
|
PID=$!
|
|
216
241
|
echo "$PID" > "$PID_FILE"
|
|
217
242
|
|
|
218
|
-
# Wait for the port to answer (max ~15s).
|
|
243
|
+
# Wait for the port to answer (max ~15s), then say what is true.
|
|
219
244
|
for _ in $(seq 1 30); do
|
|
220
245
|
if curl -s -o /dev/null "http://127.0.0.1:${PORT}/"; then
|
|
221
246
|
echo "Hlidskjalf raised (pid $PID) → http://127.0.0.1:${PORT}/"
|
|
@@ -225,5 +250,6 @@ for _ in $(seq 1 30); do
|
|
|
225
250
|
sleep 0.5
|
|
226
251
|
done
|
|
227
252
|
|
|
228
|
-
echo "Hlidskjalf started (pid $PID) but
|
|
253
|
+
echo "Hlidskjalf started (pid $PID) but :${PORT} did not answer — see $LOG" >&2
|
|
254
|
+
tail -5 "$LOG" >&2 2>/dev/null || true
|
|
229
255
|
exit 1
|