home-hosted 0.7.12 → 0.7.14
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.md +1 -0
- package/README.md +6 -3
- package/dist/cli.js +232 -69
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/uis/stock/dist/ui.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -23,6 +23,7 @@ seed entry, or a path that names one.
|
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
25
|
pnpm run up|down|restart|status # detached; `down` asks /_hh/shutdown, signals are the fallback
|
|
26
|
+
# `restart <id>` restarts one server via /_hh, panel stays up
|
|
26
27
|
pnpm run start # up --foreground (systemd, docker, a foreground shell)
|
|
27
28
|
pnpm exec tsx src/cli.ts logs # the panel's own console output (--lines, --follow, --json)
|
|
28
29
|
pnpm dev # tsx-watch panel :6000 + the stock UI's Vite :6001 (proxies /api), state in .dev-state/
|
package/README.md
CHANGED
|
@@ -340,10 +340,11 @@ restarts itself), and how hand-edits are validated: [SERVERS.md](./docs/SERVERS.
|
|
|
340
340
|
| --- | --- |
|
|
341
341
|
| `home-hosted up` | start the panel detached, and keep it alive in the background |
|
|
342
342
|
| `home-hosted down` | stop it cleanly — supervised processes included, persistent entries left running |
|
|
343
|
-
| `home-hosted restart` | `down`, then `up` |
|
|
343
|
+
| `home-hosted restart` | `down`, then `up` — or `restart <id>` to restart one server, leaving the panel up |
|
|
344
344
|
| `home-hosted status` | pid, URL, health, uptime, state and log paths (`--json` for scripts) |
|
|
345
345
|
| `home-hosted start <id>` | start one server in the default workspace — and anything it `dependsOn` (`--workspace <id>`) |
|
|
346
346
|
| `home-hosted stop <id>` | stop one server, nothing else (`--workspace <id>`) |
|
|
347
|
+
| `home-hosted restart <id>` | restart one server, nothing else (`--workspace <id>`) — the same as the panel's per-server Restart |
|
|
347
348
|
| `home-hosted logs` | read the panel's own console output — the file `up` redirects it into (`--lines`, `--follow`, `--json`) |
|
|
348
349
|
| `home-hosted set-password` | set the panel password without opening a browser |
|
|
349
350
|
| `home-hosted set-token` | set the API token scripts and agents use (`--generate`, `--clear`) |
|
|
@@ -359,12 +360,13 @@ restarts itself), and how hand-edits are validated: [SERVERS.md](./docs/SERVERS.
|
|
|
359
360
|
`home-hosted <command> --help` prints what that command takes.
|
|
360
361
|
|
|
361
362
|
```text
|
|
362
|
-
up
|
|
363
|
+
up -c/--config -p/--port --host --open --no-autostart --foreground --print-config
|
|
363
364
|
|
|
364
365
|
down (no flags)
|
|
365
366
|
status --json
|
|
366
367
|
logs --lines <n> --follow --json
|
|
367
368
|
start, stop <id> [-w/--workspace <id>] (both need the panel up)
|
|
369
|
+
restart [<id>] [-w/--workspace <id>] (no id: the panel; with one: that server)
|
|
368
370
|
init --dir --name --pm --no-install -y/--yes
|
|
369
371
|
set-password --clear
|
|
370
372
|
set-token --generate --clear
|
|
@@ -377,7 +379,8 @@ every command --home <dir> --project <dir> (or $HHOSTED_HOME, $HHOSTED
|
|
|
377
379
|
env vars HHOSTED_PASSWORD, HHOSTED_MIGRATE=allow, HHOSTED_TOKEN, GITHUB_TOKEN or GH_TOKEN
|
|
378
380
|
```
|
|
379
381
|
|
|
380
|
-
`up` and `restart` share the same flags: `restart` is `down`, then `up` with exactly what it was
|
|
382
|
+
`up` and `restart` share the same flags: bare `restart` is `down`, then `up` with exactly what it was
|
|
383
|
+
given. Given a server id — `restart web` — it restarts only that entry, and the panel keeps running.
|
|
381
384
|
`-c/--config` is the **default workspace's** servers file (`<state>/.hh/default/servers.config.json`),
|
|
382
385
|
for a launcher that pins one; a workspace picked in the UI keeps its own. `start`/`stop` omit
|
|
383
386
|
`--workspace` to act in the panel's default workspace.
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { defineCommand, runCommand } from "citty";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import readline from "node:readline";
|
|
7
|
-
import { exec, execFile, spawn, spawnSync } from "node:child_process";
|
|
7
|
+
import { exec, execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
8
8
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
9
9
|
import os from "node:os";
|
|
10
10
|
import { describeRoute, openAPIRouteHandler, resolver, validator } from "hono-openapi";
|
|
@@ -3299,6 +3299,18 @@ function createControlRoute(deps) {
|
|
|
3299
3299
|
assertLocalCall(c, "start or stop a server");
|
|
3300
3300
|
const result = await requireWorkspace(deps.panel.requireWorkspace.bind(deps.panel), c.req.valid("query").workspace).supervisor.stop(c.req.param("id"));
|
|
3301
3301
|
return c.json(result, statusForAction(result));
|
|
3302
|
+
}).post("/servers/:id/restart", describeRoute({
|
|
3303
|
+
tags: ["panel"],
|
|
3304
|
+
summary: "Restart one server, without a session (local token required)",
|
|
3305
|
+
responses: {
|
|
3306
|
+
200: { description: "Restarted" },
|
|
3307
|
+
403: { description: "Bad token, or not a local caller" },
|
|
3308
|
+
404: { description: "No such server" }
|
|
3309
|
+
}
|
|
3310
|
+
}), validate("query", workspaceQuerySchema), async (c) => {
|
|
3311
|
+
assertLocalCall(c, "restart a server");
|
|
3312
|
+
const result = await requireWorkspace(deps.panel.requireWorkspace.bind(deps.panel), c.req.valid("query").workspace).supervisor.restart(c.req.param("id"));
|
|
3313
|
+
return c.json(result, statusForAction(result));
|
|
3302
3314
|
});
|
|
3303
3315
|
}
|
|
3304
3316
|
var init_control = __esmMin((() => {
|
|
@@ -14091,9 +14103,34 @@ var init_control_server = __esmMin((() => {
|
|
|
14091
14103
|
}));
|
|
14092
14104
|
//#endregion
|
|
14093
14105
|
//#region src/providers/host.ts
|
|
14106
|
+
/**
|
|
14107
|
+
* Whether a cached reading may be reused.
|
|
14108
|
+
*
|
|
14109
|
+
* Separate from the reading itself so the rule can be tested anywhere — the read is
|
|
14110
|
+
* `sysctl`/PowerShell, which no CI runner but macOS and Windows can perform, and a bug here
|
|
14111
|
+
* would be one that only shows up as either a stale alert or a spawn storm.
|
|
14112
|
+
*
|
|
14113
|
+
* A stamp in the future (a backwards wall-clock step) counts as stale: with a plain
|
|
14114
|
+
* `now - at < ttl` it computes a negative age, which is "fresh", and the reading would stick
|
|
14115
|
+
* until wall time caught back up — potentially hours after an NTP correction.
|
|
14116
|
+
*/
|
|
14117
|
+
function isSwapCacheFresh(cache, now, ttlMs = SWAP_CACHE_MS) {
|
|
14118
|
+
if (cache === null) return false;
|
|
14119
|
+
const age = now - cache.at;
|
|
14120
|
+
return age >= 0 && age < ttlMs;
|
|
14121
|
+
}
|
|
14094
14122
|
/** Swap usage per platform: /proc on Linux, sysctl on macOS, CIM on Windows. */
|
|
14095
|
-
async function swapUsedPercent() {
|
|
14123
|
+
async function swapUsedPercent(now = Date.now()) {
|
|
14096
14124
|
if (process.platform === "linux") return 0;
|
|
14125
|
+
if (isSwapCacheFresh(swapCache, now)) return swapCache.percent;
|
|
14126
|
+
const percent = await readSwapUsedPercent();
|
|
14127
|
+
swapCache = {
|
|
14128
|
+
at: now,
|
|
14129
|
+
percent
|
|
14130
|
+
};
|
|
14131
|
+
return percent;
|
|
14132
|
+
}
|
|
14133
|
+
async function readSwapUsedPercent() {
|
|
14097
14134
|
if (process.platform === "darwin") try {
|
|
14098
14135
|
const { stdout } = await execFileAsync("sysctl", ["-n", "vm.swapusage"], { timeout: 3e3 });
|
|
14099
14136
|
const total = /total\s*=\s*([\d.]+)M/.exec(stdout)?.[1];
|
|
@@ -14119,7 +14156,20 @@ async function swapUsedPercent() {
|
|
|
14119
14156
|
}
|
|
14120
14157
|
return 0;
|
|
14121
14158
|
}
|
|
14122
|
-
/**
|
|
14159
|
+
/**
|
|
14160
|
+
* Memory used, as a percentage of what the machine actually has.
|
|
14161
|
+
*
|
|
14162
|
+
* The definition matters more than the arithmetic, and it differs per platform:
|
|
14163
|
+
*
|
|
14164
|
+
* - Linux: `/proc/meminfo`'s `MemAvailable` already excludes cache, because Linux counts
|
|
14165
|
+
* page cache as reclaimable. `os.freemem()` happens to read the same value here.
|
|
14166
|
+
* - macOS: **`os.freemem()` is wrong for this.** libuv reports `host_statistics64`'s
|
|
14167
|
+
* `free_count` alone, while macOS keeps as much RAM as it can in *inactive* and
|
|
14168
|
+
* speculative* pages — file cache it drops on demand. A perfectly healthy Mac therefore
|
|
14169
|
+
* reads as 90%+ used, which is the report a user brought back from a 0.7.3 panel. Read
|
|
14170
|
+
* `vm_stat` and count what macOS itself counts as available.
|
|
14171
|
+
* - Windows: `os.freemem()` is the free physical page count, which is the ordinary reading.
|
|
14172
|
+
*/
|
|
14123
14173
|
function memoryInfo() {
|
|
14124
14174
|
try {
|
|
14125
14175
|
const info = fs.readFileSync("/proc/meminfo", "utf8");
|
|
@@ -14134,14 +14184,81 @@ function memoryInfo() {
|
|
|
14134
14184
|
};
|
|
14135
14185
|
} catch {
|
|
14136
14186
|
const total = os.totalmem();
|
|
14137
|
-
const
|
|
14187
|
+
const available = availableMemoryBytes(process.platform, os.freemem());
|
|
14138
14188
|
return {
|
|
14139
|
-
memoryUsedPercent: total > 0 ? (total -
|
|
14189
|
+
memoryUsedPercent: total > 0 && available !== null ? Math.max(0, Math.min(100, (total - available) / total * 100)) : 0,
|
|
14140
14190
|
swapUsedPercent: 0
|
|
14141
14191
|
};
|
|
14142
14192
|
}
|
|
14143
14193
|
}
|
|
14144
14194
|
/**
|
|
14195
|
+
* Which "available memory" reading a platform's fallback should use.
|
|
14196
|
+
*
|
|
14197
|
+
* Split out from `memoryInfo` because the branch it decides is otherwise unreachable off
|
|
14198
|
+
* macOS: Linux answers from `/proc/meminfo` first, so the fallback never runs there and no
|
|
14199
|
+
* Linux test can prove which reader was chosen. Taking the platform and the OS reading as
|
|
14200
|
+
* arguments makes the decision testable on any runner, which matters — using the wrong
|
|
14201
|
+
* reader here is the whole macOS bug.
|
|
14202
|
+
*/
|
|
14203
|
+
function availableMemoryBytes(platform, osFree, vmStat) {
|
|
14204
|
+
if (platform !== "darwin") return osFree;
|
|
14205
|
+
return macAvailableBytes(vmStat);
|
|
14206
|
+
}
|
|
14207
|
+
/**
|
|
14208
|
+
* What macOS counts as available: free + inactive + speculative pages.
|
|
14209
|
+
*
|
|
14210
|
+
* `vm_stat` reports pages, so the page size is needed — and it is not always 4096 (Apple
|
|
14211
|
+
* silicon uses 16384 for the physical page size, and `vm_stat` has printed a `page size of`
|
|
14212
|
+
* header since macOS 11). Both are handled: the header when it is there, `getpagesize` when
|
|
14213
|
+
* it is not.
|
|
14214
|
+
*
|
|
14215
|
+
* Returns `null` when the reading cannot be taken, so the caller reports "unknown" rather
|
|
14216
|
+
* than inventing a percentage.
|
|
14217
|
+
*/
|
|
14218
|
+
function macAvailableBytes(vmStat) {
|
|
14219
|
+
try {
|
|
14220
|
+
const text = vmStat ?? execFileSync("vm_stat", {
|
|
14221
|
+
timeout: 3e3,
|
|
14222
|
+
encoding: "utf8"
|
|
14223
|
+
});
|
|
14224
|
+
const pageSize = Number.parseInt(/page size of (\d+) bytes/.exec(text)?.[1] ?? "", 10);
|
|
14225
|
+
const pages = (label) => {
|
|
14226
|
+
const found = new RegExp(`^${label}:\\s+(\\d+)`, "m").exec(text)?.[1];
|
|
14227
|
+
return Number.parseInt(found ?? "", 10);
|
|
14228
|
+
};
|
|
14229
|
+
const size = Number.isFinite(pageSize) && pageSize > 0 ? pageSize : getPageSize();
|
|
14230
|
+
if (!Number.isFinite(size) || size <= 0) return null;
|
|
14231
|
+
let totalPages = 0;
|
|
14232
|
+
let sawAny = false;
|
|
14233
|
+
for (const label of [
|
|
14234
|
+
"Pages free",
|
|
14235
|
+
"Pages inactive",
|
|
14236
|
+
"Pages speculative",
|
|
14237
|
+
"Pages purgeable"
|
|
14238
|
+
]) {
|
|
14239
|
+
const value = pages(label);
|
|
14240
|
+
if (Number.isFinite(value)) {
|
|
14241
|
+
totalPages += value;
|
|
14242
|
+
sawAny = true;
|
|
14243
|
+
}
|
|
14244
|
+
}
|
|
14245
|
+
return sawAny ? totalPages * size : null;
|
|
14246
|
+
} catch {
|
|
14247
|
+
return null;
|
|
14248
|
+
}
|
|
14249
|
+
}
|
|
14250
|
+
/** The OS page size, without adding a dependency for one sysconf. */
|
|
14251
|
+
function getPageSize() {
|
|
14252
|
+
try {
|
|
14253
|
+
return Number.parseInt(execFileSync("getconf", ["PAGESIZE"], {
|
|
14254
|
+
timeout: 3e3,
|
|
14255
|
+
encoding: "utf8"
|
|
14256
|
+
}).trim(), 10);
|
|
14257
|
+
} catch {
|
|
14258
|
+
return NaN;
|
|
14259
|
+
}
|
|
14260
|
+
}
|
|
14261
|
+
/**
|
|
14145
14262
|
* Best-effort CPU temperature from Linux thermal zones / hwmon. macOS and
|
|
14146
14263
|
* Windows expose no unprivileged sensor, so those platforms report null and the
|
|
14147
14264
|
* UI simply hides the reading.
|
|
@@ -14233,9 +14350,11 @@ function emptyHostView(config) {
|
|
|
14233
14350
|
sampledAt: null
|
|
14234
14351
|
};
|
|
14235
14352
|
}
|
|
14236
|
-
var execFileAsync;
|
|
14353
|
+
var execFileAsync, SWAP_CACHE_MS, swapCache;
|
|
14237
14354
|
var init_host = __esmMin((() => {
|
|
14238
14355
|
execFileAsync = promisify(execFile);
|
|
14356
|
+
SWAP_CACHE_MS = 6e4;
|
|
14357
|
+
swapCache = null;
|
|
14239
14358
|
}));
|
|
14240
14359
|
//#endregion
|
|
14241
14360
|
//#region src/services/host-monitor.ts
|
|
@@ -16782,17 +16901,107 @@ var init_down = __esmMin((() => {
|
|
|
16782
16901
|
});
|
|
16783
16902
|
}));
|
|
16784
16903
|
//#endregion
|
|
16904
|
+
//#region src/cli/server.ts
|
|
16905
|
+
var server_exports = /* @__PURE__ */ __exportAll({
|
|
16906
|
+
restartServerCommand: () => restartServerCommand,
|
|
16907
|
+
runServerAction: () => runServerAction,
|
|
16908
|
+
startCommand: () => startCommand,
|
|
16909
|
+
stopCommand: () => stopCommand
|
|
16910
|
+
});
|
|
16911
|
+
async function runServerAction(action, id, workspace) {
|
|
16912
|
+
const { clearRuntime, isProcessAlive, readRuntime, requestControl } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
|
|
16913
|
+
const runtime = readRuntime();
|
|
16914
|
+
if (runtime === null || !isProcessAlive(runtime.pid)) {
|
|
16915
|
+
if (runtime !== null) clearRuntime();
|
|
16916
|
+
fail("home-hosted is not running — start it with `home-hosted up`");
|
|
16917
|
+
}
|
|
16918
|
+
const query = workspace === void 0 || workspace.length === 0 ? "" : `?workspace=${encodeURIComponent(workspace)}`;
|
|
16919
|
+
const answer = await requestControl(runtime, `/_hh/servers/${encodeURIComponent(id)}/${action}${query}`);
|
|
16920
|
+
if (answer === null) fail(`the control panel is not answering on ${runtime.probeUrl}`);
|
|
16921
|
+
const body = answer.body ?? {};
|
|
16922
|
+
if (body.ok !== true) {
|
|
16923
|
+
const reason = body.error ?? body.message;
|
|
16924
|
+
if (reason !== void 0) fail(reason);
|
|
16925
|
+
if (answer.status === 404) fail(`the panel on ${runtime.url} is home-hosted ${runtime.version}, which does not know this command — restart it on this release`);
|
|
16926
|
+
fail(`the panel answered ${answer.status}`);
|
|
16927
|
+
}
|
|
16928
|
+
const verb = action === "start" ? "started" : action === "stop" ? "stopped" : "restarted";
|
|
16929
|
+
process.stdout.write(`${green(verb)} ${workspace === void 0 ? "" : `${workspace}/`}${id}\n`);
|
|
16930
|
+
}
|
|
16931
|
+
function serverCommand(action) {
|
|
16932
|
+
return defineCommand({
|
|
16933
|
+
meta: {
|
|
16934
|
+
name: action,
|
|
16935
|
+
description: DESCRIPTION[action]
|
|
16936
|
+
},
|
|
16937
|
+
args: {
|
|
16938
|
+
id: {
|
|
16939
|
+
type: "positional",
|
|
16940
|
+
required: false,
|
|
16941
|
+
description: "the server id from the workspace's servers.config.json"
|
|
16942
|
+
},
|
|
16943
|
+
workspace: {
|
|
16944
|
+
type: "string",
|
|
16945
|
+
alias: "w",
|
|
16946
|
+
description: "the workspace to act in (default: the panel's default workspace)"
|
|
16947
|
+
}
|
|
16948
|
+
},
|
|
16949
|
+
run: async ({ args }) => {
|
|
16950
|
+
const id = typeof args.id === "string" ? args.id.trim() : "";
|
|
16951
|
+
if (id.length === 0) fail(`${action} needs a server id — see \`home-hosted status\` or the workspace's servers.config.json`);
|
|
16952
|
+
await runServerAction(action, id, typeof args.workspace === "string" ? args.workspace.trim() : void 0);
|
|
16953
|
+
}
|
|
16954
|
+
});
|
|
16955
|
+
}
|
|
16956
|
+
var DESCRIPTION, startCommand, stopCommand, restartServerCommand;
|
|
16957
|
+
var init_server = __esmMin((() => {
|
|
16958
|
+
init_io();
|
|
16959
|
+
DESCRIPTION = {
|
|
16960
|
+
start: "start one server (the panel keeps running)",
|
|
16961
|
+
stop: "stop one server (the panel keeps running)",
|
|
16962
|
+
restart: "restart one server (the panel keeps running)"
|
|
16963
|
+
};
|
|
16964
|
+
startCommand = serverCommand("start");
|
|
16965
|
+
stopCommand = serverCommand("stop");
|
|
16966
|
+
restartServerCommand = serverCommand("restart");
|
|
16967
|
+
}));
|
|
16968
|
+
//#endregion
|
|
16785
16969
|
//#region src/cli/restart.ts
|
|
16786
16970
|
var restart_exports = /* @__PURE__ */ __exportAll({ restartCommand: () => restartCommand });
|
|
16787
|
-
/**
|
|
16971
|
+
/**
|
|
16972
|
+
* `restart` restarts the panel (`down`, then `up`) — or, given a server id, just that entry.
|
|
16973
|
+
*
|
|
16974
|
+
* The id is optional on purpose. `restart` already meant the panel, and it still does when
|
|
16975
|
+
* called the way it always was; adding a positional would have been a hard error before this
|
|
16976
|
+
* ("Unexpected argument 'web'"), so nothing existing changes meaning. The per-server case is
|
|
16977
|
+
* what the UI's Restart button has always done and the shell could not.
|
|
16978
|
+
*/
|
|
16788
16979
|
function restartCommand(entry) {
|
|
16789
16980
|
return defineCommand({
|
|
16790
16981
|
meta: {
|
|
16791
16982
|
name: "restart",
|
|
16792
|
-
description: "down
|
|
16983
|
+
description: "down then up — or one server, given an id"
|
|
16984
|
+
},
|
|
16985
|
+
args: {
|
|
16986
|
+
...upArgs,
|
|
16987
|
+
id: {
|
|
16988
|
+
type: "positional",
|
|
16989
|
+
required: false,
|
|
16990
|
+
description: "restart just this server instead of the panel"
|
|
16991
|
+
},
|
|
16992
|
+
workspace: {
|
|
16993
|
+
type: "string",
|
|
16994
|
+
alias: "w",
|
|
16995
|
+
description: "the workspace to act in (default: the panel's default workspace)"
|
|
16996
|
+
}
|
|
16793
16997
|
},
|
|
16794
|
-
args: upArgs,
|
|
16795
16998
|
run: async ({ args }) => {
|
|
16999
|
+
const id = typeof args.id === "string" ? args.id.trim() : "";
|
|
17000
|
+
if (id.length > 0) {
|
|
17001
|
+
await runServerAction("restart", id, typeof args.workspace === "string" ? args.workspace.trim() : void 0);
|
|
17002
|
+
return;
|
|
17003
|
+
}
|
|
17004
|
+
if (args.workspace !== void 0) fail("restart --workspace needs a server id — `home-hosted restart <id> --workspace <id>`");
|
|
16796
17005
|
await runDown();
|
|
16797
17006
|
await runUp(toUpFlags(args), entry);
|
|
16798
17007
|
}
|
|
@@ -16800,6 +17009,8 @@ function restartCommand(entry) {
|
|
|
16800
17009
|
}
|
|
16801
17010
|
var init_restart = __esmMin((() => {
|
|
16802
17011
|
init_down();
|
|
17012
|
+
init_io();
|
|
17013
|
+
init_server();
|
|
16803
17014
|
init_up();
|
|
16804
17015
|
}));
|
|
16805
17016
|
//#endregion
|
|
@@ -16958,63 +17169,6 @@ var init_logs = __esmMin((() => {
|
|
|
16958
17169
|
});
|
|
16959
17170
|
}));
|
|
16960
17171
|
//#endregion
|
|
16961
|
-
//#region src/cli/server.ts
|
|
16962
|
-
var server_exports = /* @__PURE__ */ __exportAll({
|
|
16963
|
-
runServerAction: () => runServerAction,
|
|
16964
|
-
startCommand: () => startCommand,
|
|
16965
|
-
stopCommand: () => stopCommand
|
|
16966
|
-
});
|
|
16967
|
-
async function runServerAction(action, id, workspace) {
|
|
16968
|
-
const { clearRuntime, isProcessAlive, readRuntime, requestControl } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
|
|
16969
|
-
const runtime = readRuntime();
|
|
16970
|
-
if (runtime === null || !isProcessAlive(runtime.pid)) {
|
|
16971
|
-
if (runtime !== null) clearRuntime();
|
|
16972
|
-
fail("home-hosted is not running — start it with `home-hosted up`");
|
|
16973
|
-
}
|
|
16974
|
-
const query = workspace === void 0 || workspace.length === 0 ? "" : `?workspace=${encodeURIComponent(workspace)}`;
|
|
16975
|
-
const answer = await requestControl(runtime, `/_hh/servers/${encodeURIComponent(id)}/${action}${query}`);
|
|
16976
|
-
if (answer === null) fail(`the control panel is not answering on ${runtime.probeUrl}`);
|
|
16977
|
-
const body = answer.body ?? {};
|
|
16978
|
-
if (body.ok !== true) {
|
|
16979
|
-
const reason = body.error ?? body.message;
|
|
16980
|
-
if (reason !== void 0) fail(reason);
|
|
16981
|
-
if (answer.status === 404) fail(`the panel on ${runtime.url} is home-hosted ${runtime.version}, which does not know this command — restart it on this release`);
|
|
16982
|
-
fail(`the panel answered ${answer.status}`);
|
|
16983
|
-
}
|
|
16984
|
-
process.stdout.write(`${green(action === "start" ? "started" : "stopped")} ${workspace === void 0 ? "" : `${workspace}/`}${id}\n`);
|
|
16985
|
-
}
|
|
16986
|
-
function serverCommand(action) {
|
|
16987
|
-
return defineCommand({
|
|
16988
|
-
meta: {
|
|
16989
|
-
name: action,
|
|
16990
|
-
description: action === "start" ? "start one server (the panel keeps running)" : "stop one server (the panel keeps running)"
|
|
16991
|
-
},
|
|
16992
|
-
args: {
|
|
16993
|
-
id: {
|
|
16994
|
-
type: "positional",
|
|
16995
|
-
required: false,
|
|
16996
|
-
description: "the server id from the workspace's servers.config.json"
|
|
16997
|
-
},
|
|
16998
|
-
workspace: {
|
|
16999
|
-
type: "string",
|
|
17000
|
-
alias: "w",
|
|
17001
|
-
description: "the workspace to act in (default: the panel's default workspace)"
|
|
17002
|
-
}
|
|
17003
|
-
},
|
|
17004
|
-
run: async ({ args }) => {
|
|
17005
|
-
const id = typeof args.id === "string" ? args.id.trim() : "";
|
|
17006
|
-
if (id.length === 0) fail(`${action} needs a server id — see \`home-hosted status\` or the workspace's servers.config.json`);
|
|
17007
|
-
await runServerAction(action, id, typeof args.workspace === "string" ? args.workspace.trim() : void 0);
|
|
17008
|
-
}
|
|
17009
|
-
});
|
|
17010
|
-
}
|
|
17011
|
-
var startCommand, stopCommand;
|
|
17012
|
-
var init_server = __esmMin((() => {
|
|
17013
|
-
init_io();
|
|
17014
|
-
startCommand = serverCommand("start");
|
|
17015
|
-
stopCommand = serverCommand("stop");
|
|
17016
|
-
}));
|
|
17017
|
-
//#endregion
|
|
17018
17172
|
//#region src/cli/set-password.ts
|
|
17019
17173
|
var set_password_exports = /* @__PURE__ */ __exportAll({
|
|
17020
17174
|
runSetPassword: () => runSetPassword,
|
|
@@ -18131,7 +18285,7 @@ var HEADER = "home-hosted — a control panel for the processes on your home ser
|
|
|
18131
18285
|
var SYNOPSIS = {
|
|
18132
18286
|
"up": "home-hosted up [options]",
|
|
18133
18287
|
"down": "home-hosted down",
|
|
18134
|
-
"restart": "home-hosted restart [options]",
|
|
18288
|
+
"restart": "home-hosted restart [<id>] [options]",
|
|
18135
18289
|
"status": "home-hosted status [--json]",
|
|
18136
18290
|
"logs": "home-hosted logs [--lines <n>] [--follow] [--json]",
|
|
18137
18291
|
"start": "home-hosted start <id> [--workspace <id>]",
|
|
@@ -18147,7 +18301,7 @@ var SYNOPSIS = {
|
|
|
18147
18301
|
var SUMMARIES = {
|
|
18148
18302
|
"up": "start it in the background (detached)",
|
|
18149
18303
|
"down": "stop it, and everything it supervises",
|
|
18150
|
-
"restart": "down
|
|
18304
|
+
"restart": "down then up — or one server, given an id",
|
|
18151
18305
|
"status": "is it running, where, and how to reach it",
|
|
18152
18306
|
"logs": "show the panel's own console output",
|
|
18153
18307
|
"start": "start one server, leaving the panel up",
|
|
@@ -18190,6 +18344,14 @@ var SERVER_SECTION = {
|
|
|
18190
18344
|
heading: "Arguments for start/stop",
|
|
18191
18345
|
lines: [["<id>", "the server id from servers.config.json"]]
|
|
18192
18346
|
};
|
|
18347
|
+
var RESTART_SECTION = {
|
|
18348
|
+
heading: "Options for restart",
|
|
18349
|
+
lines: [
|
|
18350
|
+
["<id>", "restart just this server, leaving the panel up (optional)"],
|
|
18351
|
+
["-w, --workspace <id>", "the workspace to act in, with an id"],
|
|
18352
|
+
...UP_SECTION.lines
|
|
18353
|
+
]
|
|
18354
|
+
};
|
|
18193
18355
|
var SET_PASSWORD_SECTION = {
|
|
18194
18356
|
heading: "Options for set-password",
|
|
18195
18357
|
lines: [["--clear", "remove the password, which disables authentication"]]
|
|
@@ -18309,6 +18471,7 @@ var UP_COMMANDS = /* @__PURE__ */ new Set(["up", "restart"]);
|
|
|
18309
18471
|
var SECTIONS = {
|
|
18310
18472
|
"status": STATUS_SECTION,
|
|
18311
18473
|
"logs": LOGS_SECTION,
|
|
18474
|
+
"restart": RESTART_SECTION,
|
|
18312
18475
|
"start": SERVER_SECTION,
|
|
18313
18476
|
"stop": SERVER_SECTION,
|
|
18314
18477
|
"set-password": SET_PASSWORD_SECTION,
|
|
@@ -18326,7 +18489,7 @@ var SECTIONS = {
|
|
|
18326
18489
|
function commandHelp(command) {
|
|
18327
18490
|
const synopsis = SYNOPSIS[command];
|
|
18328
18491
|
if (synopsis === void 0) return USAGE;
|
|
18329
|
-
const section = UP_COMMANDS.has(command) ? UP_SECTION :
|
|
18492
|
+
const section = SECTIONS[command] ?? (UP_COMMANDS.has(command) ? UP_SECTION : void 0);
|
|
18330
18493
|
const options = section === void 0 ? dim("no options") : renderSection(section);
|
|
18331
18494
|
return [
|
|
18332
18495
|
dim(HEADER),
|