home-hosted 0.4.0 → 0.5.0

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.
@@ -26,7 +26,7 @@ servers and its state together.
26
26
  | `command`, `args`, `cwd` | what to run, with `{placeholders}` resolved per entry |
27
27
  | `env`, `dataEnvs`, `envFile` | environment; `dataEnvs` also marks data directories for backups, `envFile` keeps secrets out of the config |
28
28
  | `port`, `bind` | enables the readiness wait, health checks and the conflict preflight; `local` keeps it on `127.0.0.1` |
29
- | `onPortConflict` | `block` (default), `warn`, or `adopt` — see below |
29
+ | `onPortConflict` | `block` (default), `warn`, `follow`, or `reclaim` — see below |
30
30
  | `health.mode` | `port` (TCP connect) or `http` (path, expected status, expected body) |
31
31
  | `health.unhealthyThreshold`, `forceRestartAfterMs` | how many failed probes before the card warns, and when to restart anyway |
32
32
  | `restart.*` | backoff: `maxRetries`, `baseDelayMs`, `factor`, `maxDelayMs`, `resetAfterMs` |
@@ -34,7 +34,7 @@ servers and its state together.
34
34
  | `dependsOn` | ids that must be healthy first; stopped in reverse order |
35
35
  | `resources.maxRssBytes` | restart when the process tree grows past a limit |
36
36
  | `bootstrap` | one command to run once before the first start (migrations, warmups) |
37
- | `backupPaths` | extra paths this entry owns, included in backups |
37
+ | `backupPaths`, `backupIgnoreGenerated` | extra paths this entry owns, included in backups; the flag (on by default) skips the known build and dependency directories inside them — `node_modules`, `dist`, `.next`, framework caches |
38
38
 
39
39
  ### Placeholders
40
40
 
@@ -96,6 +96,17 @@ set onPortConflict to "follow" to adopt it, or "reclaim" to replace it with a su
96
96
 
97
97
  ## Editing fields
98
98
 
99
- Changes from the panel are atomic and validated before they are written. Editing an entry in the file
100
- by hand is picked up without a restart; a value the schema rejects is refused with the exact path, and
101
- the panel keeps running on the config it already had.
99
+ Changes from the panel are atomic and validated before they are written. Editing the file by hand —
100
+ an editor, a `git checkout`, a config-management tool — is picked up within a couple of seconds, no
101
+ restart needed:
102
+
103
+ - **A definition you changed** takes effect on that entry's next start; a running process is not
104
+ restarted under you.
105
+ - **A definition you added** appears (and starts itself if it is `autostart`); **one you removed** is
106
+ stopped and forgotten.
107
+ - **A file the schema rejects, or one that cannot be parsed**, is reported in the panel with the
108
+ exact path and the running config is left alone. Fix it and it reloads on its own — a typo never
109
+ stops a server.
110
+
111
+ A save from the panel's own UI is the panel's write: it replaces the file, so hand-edits made while
112
+ it runs are lost by that next save. Edit, then let the panel read it back, before using the UI.
@@ -11,12 +11,12 @@ specific about what you want:
11
11
 
12
12
  > Help me build a UI for `home-hosted`: a nostalgic game theme. Servers as a party menu, health as
13
13
  > HP bars, logs in a text-box pane, keyboard navigation, and a save-state corner for backups.
14
- > Follow `UI_CREATION.md`.
14
+ > Follow `docs/UI_CREATION.md`.
15
15
 
16
16
  Then zip the build and install it (below). Useful constraints to include in the prompt: the API is
17
17
  same-origin (relative `/api/...`), `401 { code: 'AUTH_REQUIRED' }` means "show the login screen",
18
18
  live data should come from SSE, and assets must be self-hosted. Existing directions to borrow from
19
- live in [`docs/mockups/`](./docs/mockups) — or ask for something else entirely; the server does not
19
+ live in [`mockups/`](./mockups) — or ask for something else entirely; the server does not
20
20
  care what your UI looks like.
21
21
 
22
22
  ## Install it
@@ -33,6 +33,9 @@ $HHOSTED_HOME/.ui/ ← where your build lives
33
33
  (`zip -r ui.zip dist` works too). `ui.json` is optional; it is what the settings page shows as
34
34
  installed.
35
35
 
36
+ Without the settings page, `home-hosted ui-switch` installs one from a GitHub release asset (its
37
+ default), from a local `.zip` (`--file ./ui.zip`), or from a URL (`--file https://…/ui.zip`).
38
+
36
39
  Nothing is built on the server side: whatever you upload is served as-is, so ship plain
37
40
  HTML/JS/CSS or the output of your own Vite/Next/Astro build with relative asset paths.
38
41
 
@@ -102,6 +105,11 @@ signed-in session. The browser app you ship should still use the cookie.
102
105
  Two settings worth reflecting: `control.label` is the panel's own name (the stock shell shows it),
103
106
  and `GET /api/settings` includes `ui` — which UI is being served, and its metadata.
104
107
 
108
+ An entry body is partial by design: `POST /api/servers` and `PATCH /api/servers/:id` take only the
109
+ fields the person decided, and the panel's **Server defaults** fill the rest. Sending a value the
110
+ person never chose freezes it against those defaults, so build the body as a diff
111
+ (`inheritBaseline` and `diffFields` in `src/shared/patch-diff.ts`).
112
+
105
113
  ### Failures
106
114
 
107
115
  Every failing request answers with one envelope:
@@ -128,6 +136,30 @@ Send `?logs=0` to skip log frames, or `?serverId=<id>` to follow one server. The
128
136
  `sseMessageSchema` in `src/shared/contracts.ts` — the server validates against it before writing, so
129
137
  that schema is also your best type source.
130
138
 
139
+ ## Editing settings without fighting the stream
140
+
141
+ Two things the API will not tell you, and both are what people report as bugs:
142
+
143
+ - **A live frame must never overwrite a half-typed edit.** Every frame carries freshly built
144
+ objects, so a watcher that copies state into a form on each change reverts whatever is being
145
+ typed. The settings are also not one payload: host thresholds and the backups policy come from
146
+ `GET /api/settings`, which lands *after* the first SSE frame — a single "has anything changed?"
147
+ guard reads that late arrival as a pending edit, leaves those fields showing schema defaults
148
+ forever, and offers to "revert" a change nobody made. Fill each block on its own, and only while
149
+ that block is untouched since it was last filled (`uis/stock/src/components/settings/settingsForm.ts`).
150
+ - **A boolean setting is a switch, not a checkbox.** Keep checkboxes for picking items out of a set
151
+ (a restore plan), where the control is the list entry. `uis/stock/src/components/ui/ToggleSwitch.vue`
152
+ is the reference.
153
+ - **Decide to open the stream from the session, not from the auth flags.** With authentication off,
154
+ `authRequired` and `authenticated` are both `false` from the first paint to the last, so a watcher
155
+ on those two never re-runs when the session lands: no event stream is opened, the connection badge
156
+ sits on "Connecting", and the dashboard shows one stale snapshot forever. Turn the two flags into a
157
+ single decision (`wait` | `connect` | `login`) and watch that — see
158
+ `uis/stock/src/composables/useSession.ts`.
159
+
160
+ A pending-edit summary pays for itself: show how many fields changed, and let the list be opened —
161
+ `describeChanges` and `countLeaves` in `src/shared/patch-diff.ts` turn a patch into those rows.
162
+
131
163
  ## Building one in this repo
132
164
 
133
165
  The repo's own UIs are Vite apps: `uis/<name>/` holds `index.html`, `src/`, a `tsconfig.json`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "home-hosted",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
5
  "packageManager": "pnpm@12.5.1",
6
6
  "description": "A self-hosted control panel that keeps your home server processes alive - with a UI you can replace.",
7
7
  "author": "NamesMT <dangquoctrung123@gmail.com>",
@@ -38,12 +38,12 @@
38
38
  "files": [
39
39
  "AGENTS.md",
40
40
  "LICENSE",
41
- "NOTIFICATIONS.md",
42
41
  "README.md",
43
- "SERVERS.md",
44
- "UI_CREATION.md",
45
42
  "bin",
46
43
  "dist",
44
+ "docs/NOTIFICATIONS.md",
45
+ "docs/SERVERS.md",
46
+ "docs/UI_CREATION.md",
47
47
  "uis/stock/dist"
48
48
  ],
49
49
  "engines": {
@@ -53,6 +53,7 @@
53
53
  "up": "tsx src/cli.ts up",
54
54
  "down": "tsx src/cli.ts down",
55
55
  "status": "tsx src/cli.ts status",
56
+ "restart": "tsx src/cli.ts restart",
56
57
  "start": "tsx src/cli.ts up --foreground",
57
58
  "dev": "node scripts/dev.mjs",
58
59
  "build": "pnpm run build:ui && pnpm run build:server",
@@ -77,6 +78,7 @@
77
78
  "@scalar/hono-api-reference": "^0.12.4",
78
79
  "@zip.js/zip.js": "^2.17.0",
79
80
  "arktype": "^2.2.3",
81
+ "citty": "^0.2.2",
80
82
  "consola": "^3.4.2",
81
83
  "grammy": "^1.46.0",
82
84
  "hono": "^4.13.8",
@@ -91,15 +93,18 @@
91
93
  "@types/node": "^24.13.3",
92
94
  "@vitejs/plugin-vue": "^6.0.9",
93
95
  "@vitest/coverage-v8": "^5.0.1",
96
+ "@vue/test-utils": "^2.5.1",
94
97
  "clsx": "^2.1.1",
95
98
  "eslint": "^10.11.0",
96
99
  "gifenc": "^1.0.3",
100
+ "happy-dom": "^20.14.5",
97
101
  "lint-staged": "^17.5.1",
98
102
  "lucide-vue-next": "^1.0.0",
99
103
  "playwright": "^1.63.0",
100
104
  "pngjs": "^7.0.0",
101
105
  "reka-ui": "^2.10.5",
102
106
  "simple-git-hooks": "^2.14.0",
107
+ "splitpanes": "^4.1.2",
103
108
  "tailwind-merge": "^3.7.0",
104
109
  "tailwindcss": "^4.3.3",
105
110
  "tsx": "^4.23.15",