@tridha643/hestia 1.1.0 → 1.3.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.
- package/README.md +3 -2
- package/dist/cli.js +1193 -527
- package/dist/cli.js.map +26 -19
- package/dist/daemon.js +121 -28
- package/dist/daemon.js.map +9 -8
- package/package.json +1 -1
- package/skills/hestia/SKILL.md +13 -11
package/dist/cli.js
CHANGED
|
@@ -8244,7 +8244,7 @@ async function composeUp(ctx, services) {
|
|
|
8244
8244
|
async function composeDown(ctx, destroy) {
|
|
8245
8245
|
const rest = ["down", "--remove-orphans"];
|
|
8246
8246
|
if (destroy)
|
|
8247
|
-
rest.push("-v");
|
|
8247
|
+
rest.push("-v", "--rmi", "local");
|
|
8248
8248
|
await docker(ctx, rest);
|
|
8249
8249
|
}
|
|
8250
8250
|
async function composePs(ctx) {
|
|
@@ -11249,6 +11249,87 @@ var init_ingress = __esm(() => {
|
|
|
11249
11249
|
init_local_http_router();
|
|
11250
11250
|
});
|
|
11251
11251
|
|
|
11252
|
+
// packages/engine/src/tunnel/orphans.ts
|
|
11253
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
11254
|
+
import { join as join21 } from "path";
|
|
11255
|
+
function hestiaTunnelMarker(uuid) {
|
|
11256
|
+
return join21(hestiaHome(), "tunnel", uuid);
|
|
11257
|
+
}
|
|
11258
|
+
function parseLocalHestiaConnectors(psOutput, marker) {
|
|
11259
|
+
const rows = [];
|
|
11260
|
+
for (const line of psOutput.split(`
|
|
11261
|
+
`)) {
|
|
11262
|
+
const trimmed = line.trim();
|
|
11263
|
+
if (trimmed === "")
|
|
11264
|
+
continue;
|
|
11265
|
+
const m = trimmed.match(/^(\d+)\s+(\d+)\s+(.*)$/);
|
|
11266
|
+
if (m === null)
|
|
11267
|
+
continue;
|
|
11268
|
+
const command = m[3];
|
|
11269
|
+
if (!command.includes(marker))
|
|
11270
|
+
continue;
|
|
11271
|
+
if (!/(^|[\/\s])cloudflared(\s|$)/.test(command))
|
|
11272
|
+
continue;
|
|
11273
|
+
rows.push({ pid: Number(m[1]), pgid: Number(m[2]), command });
|
|
11274
|
+
}
|
|
11275
|
+
return rows;
|
|
11276
|
+
}
|
|
11277
|
+
function listLocalHestiaConnectors(uuid) {
|
|
11278
|
+
let out;
|
|
11279
|
+
try {
|
|
11280
|
+
out = execFileSync4("ps", ["-Ao", "pid=,pgid=,command="], {
|
|
11281
|
+
encoding: "utf8",
|
|
11282
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
11283
|
+
env: { ...process.env, LC_ALL: "C", LANG: "C" }
|
|
11284
|
+
});
|
|
11285
|
+
} catch {
|
|
11286
|
+
return [];
|
|
11287
|
+
}
|
|
11288
|
+
return parseLocalHestiaConnectors(out, hestiaTunnelMarker(uuid));
|
|
11289
|
+
}
|
|
11290
|
+
async function reapOrphanConnectors(uuid, keep) {
|
|
11291
|
+
const procs = listLocalHestiaConnectors(uuid).filter((p) => {
|
|
11292
|
+
if (keep === undefined)
|
|
11293
|
+
return true;
|
|
11294
|
+
return p.pgid !== keep.pgid && p.pid !== keep.pid;
|
|
11295
|
+
});
|
|
11296
|
+
if (procs.length === 0)
|
|
11297
|
+
return { reapedGroups: 0, pids: [] };
|
|
11298
|
+
const groups = new Set(procs.map((p) => p.pgid));
|
|
11299
|
+
const pids = procs.map((p) => p.pid);
|
|
11300
|
+
for (const pgid of groups) {
|
|
11301
|
+
try {
|
|
11302
|
+
process.kill(-pgid, "SIGTERM");
|
|
11303
|
+
} catch {}
|
|
11304
|
+
}
|
|
11305
|
+
const deadline = Date.now() + REAP_GRACE_MS;
|
|
11306
|
+
while (Date.now() < deadline) {
|
|
11307
|
+
const still = listLocalHestiaConnectors(uuid).filter((p) => {
|
|
11308
|
+
if (keep === undefined)
|
|
11309
|
+
return true;
|
|
11310
|
+
return p.pgid !== keep.pgid && p.pid !== keep.pid;
|
|
11311
|
+
});
|
|
11312
|
+
if (still.length === 0)
|
|
11313
|
+
return { reapedGroups: groups.size, pids };
|
|
11314
|
+
await new Promise((r) => setTimeout(r, REAP_POLL_MS));
|
|
11315
|
+
}
|
|
11316
|
+
for (const p of listLocalHestiaConnectors(uuid)) {
|
|
11317
|
+
if (keep !== undefined && (p.pgid === keep.pgid || p.pid === keep.pid))
|
|
11318
|
+
continue;
|
|
11319
|
+
try {
|
|
11320
|
+
process.kill(-p.pgid, "SIGKILL");
|
|
11321
|
+
} catch {}
|
|
11322
|
+
try {
|
|
11323
|
+
process.kill(p.pid, "SIGKILL");
|
|
11324
|
+
} catch {}
|
|
11325
|
+
}
|
|
11326
|
+
return { reapedGroups: groups.size, pids };
|
|
11327
|
+
}
|
|
11328
|
+
var REAP_GRACE_MS = 5000, REAP_POLL_MS = 100;
|
|
11329
|
+
var init_orphans = __esm(() => {
|
|
11330
|
+
init_state();
|
|
11331
|
+
});
|
|
11332
|
+
|
|
11252
11333
|
// packages/engine/src/tunnel/verify.ts
|
|
11253
11334
|
async function get2(port, path) {
|
|
11254
11335
|
try {
|
|
@@ -11298,15 +11379,15 @@ import {
|
|
|
11298
11379
|
readFileSync as readFileSync17,
|
|
11299
11380
|
readdirSync as readdirSync7
|
|
11300
11381
|
} from "fs";
|
|
11301
|
-
import { join as
|
|
11382
|
+
import { join as join22 } from "path";
|
|
11302
11383
|
function tunnelDir(uuid) {
|
|
11303
|
-
return
|
|
11384
|
+
return join22(hestiaHome(), "tunnel", uuid);
|
|
11304
11385
|
}
|
|
11305
11386
|
function configPath(uuid) {
|
|
11306
|
-
return
|
|
11387
|
+
return join22(tunnelDir(uuid), "config.yml");
|
|
11307
11388
|
}
|
|
11308
11389
|
function adoptedMarker(uuid) {
|
|
11309
|
-
return
|
|
11390
|
+
return join22(tunnelDir(uuid), "adopted.json");
|
|
11310
11391
|
}
|
|
11311
11392
|
function isAdopted(uuid) {
|
|
11312
11393
|
return existsSync19(adoptedMarker(uuid));
|
|
@@ -11325,10 +11406,10 @@ function readAdopted(uuid) {
|
|
|
11325
11406
|
return { uuid, name: marker.name, credFile: marker.credFile, reconstructed: false };
|
|
11326
11407
|
}
|
|
11327
11408
|
let name;
|
|
11328
|
-
const stacksDir =
|
|
11409
|
+
const stacksDir = join22(hestiaHome(), "stacks");
|
|
11329
11410
|
if (existsSync19(stacksDir)) {
|
|
11330
11411
|
for (const project of readdirSync7(stacksDir)) {
|
|
11331
|
-
const sp =
|
|
11412
|
+
const sp = join22(stacksDir, project, "stack.json");
|
|
11332
11413
|
if (!existsSync19(sp))
|
|
11333
11414
|
continue;
|
|
11334
11415
|
try {
|
|
@@ -11348,19 +11429,19 @@ function readAdopted(uuid) {
|
|
|
11348
11429
|
};
|
|
11349
11430
|
}
|
|
11350
11431
|
function listAdopted() {
|
|
11351
|
-
const root =
|
|
11432
|
+
const root = join22(hestiaHome(), "tunnel");
|
|
11352
11433
|
if (!existsSync19(root))
|
|
11353
11434
|
return [];
|
|
11354
11435
|
return readdirSync7(root).filter((uuid) => isAdopted(uuid));
|
|
11355
11436
|
}
|
|
11356
11437
|
function collectDynamicRules(uuid) {
|
|
11357
|
-
const stacksDir =
|
|
11438
|
+
const stacksDir = join22(hestiaHome(), "stacks");
|
|
11358
11439
|
const rules = [];
|
|
11359
11440
|
const dropped = [];
|
|
11360
11441
|
if (!existsSync19(stacksDir))
|
|
11361
11442
|
return { rules, dropped };
|
|
11362
11443
|
for (const project of readdirSync7(stacksDir)) {
|
|
11363
|
-
const p =
|
|
11444
|
+
const p = join22(stacksDir, project, "stack.json");
|
|
11364
11445
|
if (!existsSync19(p))
|
|
11365
11446
|
continue;
|
|
11366
11447
|
let record;
|
|
@@ -11405,6 +11486,10 @@ async function reconcileTunnel(ref, opts) {
|
|
|
11405
11486
|
});
|
|
11406
11487
|
const pf = readPidfile(dir, CONNECTOR);
|
|
11407
11488
|
const live = pf !== null && isLive(pf);
|
|
11489
|
+
const { reapedGroups } = await reapOrphanConnectors(ref.uuid, live ? pf : undefined);
|
|
11490
|
+
if (reapedGroups > 0) {
|
|
11491
|
+
warnings.push(`reaped ${reapedGroups} orphan hestia connector process group(s) for this tunnel`);
|
|
11492
|
+
}
|
|
11408
11493
|
const currentConfig = existsSync19(cfgPath) ? readFileSync17(cfgPath, "utf8") : null;
|
|
11409
11494
|
if (live && currentConfig === nextConfig) {
|
|
11410
11495
|
return { restarted: false, metricsPort: pf.port, ready: true, warnings };
|
|
@@ -11481,6 +11566,7 @@ var init_registry = __esm(() => {
|
|
|
11481
11566
|
init_shutdown();
|
|
11482
11567
|
init_ingress();
|
|
11483
11568
|
init_shared();
|
|
11569
|
+
init_orphans();
|
|
11484
11570
|
init_atomic_json_file();
|
|
11485
11571
|
});
|
|
11486
11572
|
|
|
@@ -12142,7 +12228,7 @@ var init_shared_arbiter = __esm(() => {
|
|
|
12142
12228
|
// packages/engine/src/doctor.ts
|
|
12143
12229
|
import { existsSync as existsSync20, readFileSync as readFileSync18, readdirSync as readdirSync8 } from "fs";
|
|
12144
12230
|
import { execFile as execFile9 } from "child_process";
|
|
12145
|
-
import { dirname as dirname6, join as
|
|
12231
|
+
import { dirname as dirname6, join as join23 } from "path";
|
|
12146
12232
|
function row(check, level, detail) {
|
|
12147
12233
|
return { check, level, detail };
|
|
12148
12234
|
}
|
|
@@ -12183,8 +12269,8 @@ async function envChecks(ctx) {
|
|
|
12183
12269
|
}
|
|
12184
12270
|
if (ctx !== null) {
|
|
12185
12271
|
const ignored = await exec("git", ["-C", ctx.worktreeRoot, "check-ignore", "-q", ".hestia/"]);
|
|
12186
|
-
rows.push(ignored.ok ? row("state-ignore", "ok", ".hestia/ is ignored") : row("state-ignore", "error", `add the exact line ".hestia/" to ${
|
|
12187
|
-
const schema = existsSync20(
|
|
12272
|
+
rows.push(ignored.ok ? row("state-ignore", "ok", ".hestia/ is ignored") : row("state-ignore", "error", `add the exact line ".hestia/" to ${join23(ctx.worktreeRoot, ".gitignore")}`));
|
|
12273
|
+
const schema = existsSync20(join23(ctx.worktreeRoot, ".env.schema"));
|
|
12188
12274
|
if (schema) {
|
|
12189
12275
|
rows.push(detectVarlock(ctx.worktreeRoot) !== null ? row("varlock", "ok", "local varlock found \u2014 worker env composition active") : row("varlock", "warn", ".env.schema present but no local varlock (bun install?)"));
|
|
12190
12276
|
}
|
|
@@ -12193,7 +12279,7 @@ async function envChecks(ctx) {
|
|
|
12193
12279
|
const missing = workers.filter((w) => {
|
|
12194
12280
|
let dir = dirname6(w.configPath);
|
|
12195
12281
|
for (;; ) {
|
|
12196
|
-
if (existsSync20(
|
|
12282
|
+
if (existsSync20(join23(dir, "node_modules", ".bin", "wrangler")))
|
|
12197
12283
|
return false;
|
|
12198
12284
|
if (dir === ctx.worktreeRoot || dirname6(dir) === dir)
|
|
12199
12285
|
return true;
|
|
@@ -12252,7 +12338,7 @@ async function stateChecks(ctx) {
|
|
|
12252
12338
|
rows.push(row(`exposure:${exp.service}`, "error", `ingress rule points at port ${exp.originPort} but the service owns ` + `${publishedPort} \u2014 a recycled port serves ANOTHER worktree; ` + `run any hestia command to resync, this should not persist`));
|
|
12253
12339
|
}
|
|
12254
12340
|
}
|
|
12255
|
-
const lockPath2 =
|
|
12341
|
+
const lockPath2 = join23(hestiaDir(worktreeRoot), "lock");
|
|
12256
12342
|
if (existsSync20(lockPath2)) {
|
|
12257
12343
|
try {
|
|
12258
12344
|
const holder = JSON.parse(readFileSync18(lockPath2, "utf8"));
|
|
@@ -12263,7 +12349,7 @@ async function stateChecks(ctx) {
|
|
|
12263
12349
|
rows.push(row("lock", "warn", "unreadable worktree lock file"));
|
|
12264
12350
|
}
|
|
12265
12351
|
}
|
|
12266
|
-
const mirror =
|
|
12352
|
+
const mirror = join23(hestiaHome(), "stacks", record.project, "stack.json");
|
|
12267
12353
|
if (!existsSync20(mirror)) {
|
|
12268
12354
|
rows.push(row("mirror", "warn", "no ~/.hestia mirror \u2014 `down --project` would not work after worktree deletion"));
|
|
12269
12355
|
}
|
|
@@ -12271,11 +12357,11 @@ async function stateChecks(ctx) {
|
|
|
12271
12357
|
}
|
|
12272
12358
|
async function machineChecks() {
|
|
12273
12359
|
const rows = [];
|
|
12274
|
-
const stacksDir =
|
|
12360
|
+
const stacksDir = join23(hestiaHome(), "stacks");
|
|
12275
12361
|
const mirrored = new Set;
|
|
12276
12362
|
if (existsSync20(stacksDir)) {
|
|
12277
12363
|
for (const project of readdirSync8(stacksDir)) {
|
|
12278
|
-
const p =
|
|
12364
|
+
const p = join23(stacksDir, project, "stack.json");
|
|
12279
12365
|
if (!existsSync20(p))
|
|
12280
12366
|
continue;
|
|
12281
12367
|
mirrored.add(project);
|
|
@@ -12328,12 +12414,18 @@ async function tunnelChecks() {
|
|
|
12328
12414
|
} catch {
|
|
12329
12415
|
connectors = null;
|
|
12330
12416
|
}
|
|
12417
|
+
const local = listLocalHestiaConnectors(uuid);
|
|
12418
|
+
const localOrphans = live && pf !== null ? local.filter((p) => p.pgid !== pf.pgid && p.pid !== pf.pid) : local;
|
|
12419
|
+
if (localOrphans.length > 0) {
|
|
12420
|
+
rows.push(row(`${label}:local-orphans`, "error", `${localOrphans.length} untracked hestia connector(s) still running ` + `(pids ${localOrphans.map((p) => p.pid).join(", ")}) \u2014 the daemon sweep ` + `or next hestia command reaps them; do not start another cloudflared`));
|
|
12421
|
+
}
|
|
12331
12422
|
if (connectors === null) {
|
|
12332
12423
|
rows.push(row(`${label}:connectors`, "unknown", "cloudflare unreachable \u2014 cannot count connectors"));
|
|
12333
12424
|
} else {
|
|
12334
12425
|
const expected = live ? 1 : 0;
|
|
12335
12426
|
if (connectors > expected) {
|
|
12336
|
-
|
|
12427
|
+
const detail = localOrphans.length > 0 ? `${connectors} connector(s) registered but hestia runs ${expected} \u2014 ` + `${localOrphans.length} are local hestia-owned orphans (auto-reaped on next sweep); ` + `any remainder is a foreign replica` : `${connectors} connector(s) registered but hestia runs ${expected} \u2014 a foreign ` + `replica cross-wires worktrees; stop the other cloudflared (only processes whose ` + `argv lacks ~/.hestia/tunnel/<uuid>/ are truly foreign)`;
|
|
12428
|
+
rows.push(row(`${label}:connectors`, "error", detail));
|
|
12337
12429
|
}
|
|
12338
12430
|
}
|
|
12339
12431
|
}
|
|
@@ -12435,6 +12527,7 @@ var init_doctor = __esm(() => {
|
|
|
12435
12527
|
init_discover();
|
|
12436
12528
|
init_registry();
|
|
12437
12529
|
init_cloudflared();
|
|
12530
|
+
init_orphans();
|
|
12438
12531
|
init_client();
|
|
12439
12532
|
init_routes();
|
|
12440
12533
|
init_launchd();
|
|
@@ -12447,7 +12540,7 @@ var init_doctor = __esm(() => {
|
|
|
12447
12540
|
import { execFile as execFile10 } from "child_process";
|
|
12448
12541
|
import { promisify as promisify8 } from "util";
|
|
12449
12542
|
import { existsSync as existsSync21, readFileSync as readFileSync19, readdirSync as readdirSync9 } from "fs";
|
|
12450
|
-
import { join as
|
|
12543
|
+
import { join as join24 } from "path";
|
|
12451
12544
|
|
|
12452
12545
|
class LatestFleetChannel {
|
|
12453
12546
|
#pending;
|
|
@@ -12490,11 +12583,11 @@ class LatestFleetChannel {
|
|
|
12490
12583
|
function readManagedMirrors() {
|
|
12491
12584
|
const records = [];
|
|
12492
12585
|
const warnings = [];
|
|
12493
|
-
const stacksDirectory =
|
|
12586
|
+
const stacksDirectory = join24(hestiaHome(), "stacks");
|
|
12494
12587
|
if (!existsSync21(stacksDirectory))
|
|
12495
12588
|
return { records, warnings };
|
|
12496
12589
|
for (const project of readdirSync9(stacksDirectory).sort()) {
|
|
12497
|
-
const path =
|
|
12590
|
+
const path = join24(stacksDirectory, project, "stack.json");
|
|
12498
12591
|
try {
|
|
12499
12592
|
const source = readFileSync19(path);
|
|
12500
12593
|
if (source.byteLength > MAX_MIRROR_BYTES2) {
|
|
@@ -12982,7 +13075,7 @@ var init_init_config = __esm(() => {
|
|
|
12982
13075
|
import { existsSync as existsSync22, rmSync as rmSync10 } from "fs";
|
|
12983
13076
|
import { execFile as execFile11 } from "child_process";
|
|
12984
13077
|
import { promisify as promisify9 } from "util";
|
|
12985
|
-
import { join as
|
|
13078
|
+
import { join as join25, resolve as resolve2 } from "path";
|
|
12986
13079
|
import { createHash as createHash7 } from "crypto";
|
|
12987
13080
|
import { resolve4, resolve6, resolveCname } from "dns/promises";
|
|
12988
13081
|
function composeUnsupported(message) {
|
|
@@ -13092,7 +13185,7 @@ async function assertHestiaStateIgnored(worktreeRoot) {
|
|
|
13092
13185
|
try {
|
|
13093
13186
|
await pexec9("git", ["-C", worktreeRoot, "check-ignore", "-q", ".hestia/"], { timeout: 5000 });
|
|
13094
13187
|
} catch {
|
|
13095
|
-
throw new HestiaError("state-not-ignored", `.hestia is not ignored in ${worktreeRoot}; add the line ".hestia/" to ${
|
|
13188
|
+
throw new HestiaError("state-not-ignored", `.hestia is not ignored in ${worktreeRoot}; add the line ".hestia/" to ${join25(worktreeRoot, ".gitignore")}`, { remedy: `.hestia/`, path: join25(worktreeRoot, ".gitignore") });
|
|
13096
13189
|
}
|
|
13097
13190
|
}
|
|
13098
13191
|
async function prepareCompose(worktreeRoot, project, repo, branch, opts, configuredBaseFile) {
|
|
@@ -13109,7 +13202,7 @@ async function prepareCompose(worktreeRoot, project, repo, branch, opts, configu
|
|
|
13109
13202
|
services
|
|
13110
13203
|
});
|
|
13111
13204
|
ensureDir(hestiaDir(worktreeRoot));
|
|
13112
|
-
const overridePath =
|
|
13205
|
+
const overridePath = join25(hestiaDir(worktreeRoot), OVERRIDE_FILE);
|
|
13113
13206
|
writeAtomicTextFile(overridePath, yaml);
|
|
13114
13207
|
return {
|
|
13115
13208
|
ctx: {
|
|
@@ -13142,7 +13235,7 @@ function writeDockerfileComposeModel(worktreeRoot, workloads, conventionalCompos
|
|
|
13142
13235
|
...conventionalComposeFile === undefined ? {} : { include: [{ path: conventionalComposeFile }] },
|
|
13143
13236
|
services
|
|
13144
13237
|
};
|
|
13145
|
-
const path =
|
|
13238
|
+
const path = join25(hestiaDir(worktreeRoot), DOCKERFILE_COMPOSE_FILE);
|
|
13146
13239
|
writeAtomicTextFile(path, $stringify(model));
|
|
13147
13240
|
return path;
|
|
13148
13241
|
}
|
|
@@ -13162,7 +13255,7 @@ function freshRecord(project, repoId, repo, branch, worktree) {
|
|
|
13162
13255
|
};
|
|
13163
13256
|
}
|
|
13164
13257
|
function assertCurrentStackIdentity(record, current) {
|
|
13165
|
-
const path =
|
|
13258
|
+
const path = join25(hestiaDir(current.worktreeRoot), "stack.json");
|
|
13166
13259
|
assertMutableStackRecord(record, path);
|
|
13167
13260
|
const matches = record.repoId === current.repoId && record.repo === current.repo && record.branch === current.branch && resolve2(record.worktree) === resolve2(current.worktreeRoot) && record.project === projectName(current.repoId, current.repo, current.branch, current.worktreeRoot);
|
|
13168
13261
|
if (!matches) {
|
|
@@ -13341,7 +13434,7 @@ function matchesExpectedStack(record, expected) {
|
|
|
13341
13434
|
return record !== null && (record.repoId === undefined || record.repoId === expected.repoId) && record.worktree === expected.worktree && record.createdAt === expected.createdAt;
|
|
13342
13435
|
}
|
|
13343
13436
|
function projectMutationRoot(project) {
|
|
13344
|
-
return
|
|
13437
|
+
return join25(hestiaHome(), "project-locks", project);
|
|
13345
13438
|
}
|
|
13346
13439
|
async function assertNamedTunnelDns(hostname, tunnelUuid) {
|
|
13347
13440
|
const expected = `${tunnelUuid}.cfargotunnel.com`;
|
|
@@ -13948,13 +14041,13 @@ class ComposeEngine {
|
|
|
13948
14041
|
const composeFile = record?.composeFile ?? tryLoadConfig(worktreeRoot)?.composeFile;
|
|
13949
14042
|
if (composeFile !== undefined) {
|
|
13950
14043
|
const project2 = record?.project ?? projectName(repoId, repo, branch, worktreeRoot);
|
|
13951
|
-
const overrideFile = record?.overrideFile ??
|
|
14044
|
+
const overrideFile = record?.overrideFile ?? join25(hestiaDir(worktreeRoot), OVERRIDE_FILE);
|
|
13952
14045
|
if (existsSync22(overrideFile)) {
|
|
13953
14046
|
await composeDown({ project: project2, baseFile: composeFile, overrideFile, cwd: worktreeRoot }, opts?.destroy ?? false);
|
|
13954
14047
|
} else if (record?.composeFile !== undefined) {
|
|
13955
14048
|
const rest = ["compose", "-p", project2, "down", "--remove-orphans"];
|
|
13956
14049
|
if (opts?.destroy)
|
|
13957
|
-
rest.push("-v");
|
|
14050
|
+
rest.push("-v", "--rmi", "local");
|
|
13958
14051
|
await pexec9("docker", rest, { timeout: 180000 });
|
|
13959
14052
|
}
|
|
13960
14053
|
}
|
|
@@ -14011,7 +14104,7 @@ class ComposeEngine {
|
|
|
14011
14104
|
try {
|
|
14012
14105
|
const rest = ["compose", "-p", project, "down", "--remove-orphans"];
|
|
14013
14106
|
if (opts?.destroy)
|
|
14014
|
-
rest.push("-v");
|
|
14107
|
+
rest.push("-v", "--rmi", "local");
|
|
14015
14108
|
await pexec9("docker", rest, { timeout: 180000 });
|
|
14016
14109
|
} catch (err) {
|
|
14017
14110
|
if (fresh?.composeFile !== undefined) {
|
|
@@ -14181,7 +14274,7 @@ class ComposeEngine {
|
|
|
14181
14274
|
const alias = selection.endpoint.alias ?? selection.endpoint.name;
|
|
14182
14275
|
const authority = internalEndpointAuthority(record.project, alias);
|
|
14183
14276
|
const name = `aux-quick-${createHash7("sha256").update(alias).digest("hex").slice(0, 10)}`;
|
|
14184
|
-
const quickCfg =
|
|
14277
|
+
const quickCfg = join25(hestiaDir(worktreeRoot), `quick-${name}.yml`);
|
|
14185
14278
|
writeAtomicTextFile(quickCfg, `ingress:
|
|
14186
14279
|
- service: ${JSON.stringify(`unix:${publicGatewaySocketPath()}`)}
|
|
14187
14280
|
` + ` originRequest:
|
|
@@ -14786,7 +14879,7 @@ class DaemonFleetSource {
|
|
|
14786
14879
|
return Promise.reject(new Error(`stack ${stack.project} has no stable incarnation timestamp`));
|
|
14787
14880
|
}
|
|
14788
14881
|
return engine.downProject(stack.project, {
|
|
14789
|
-
destroy:
|
|
14882
|
+
destroy: true,
|
|
14790
14883
|
expectedStack: {
|
|
14791
14884
|
repoId: stack.repoId,
|
|
14792
14885
|
worktree: stack.worktree,
|
|
@@ -14802,6 +14895,14 @@ var init_fleet_source = __esm(() => {
|
|
|
14802
14895
|
init_src2();
|
|
14803
14896
|
});
|
|
14804
14897
|
|
|
14898
|
+
// packages/tui/src/fleet-endpoints.ts
|
|
14899
|
+
function serviceEndpoints(service) {
|
|
14900
|
+
return service.endpoints ?? (service.endpoint === undefined ? [] : [service.endpoint]);
|
|
14901
|
+
}
|
|
14902
|
+
function endpointReach(endpoint) {
|
|
14903
|
+
return endpoint.localUrl ?? endpoint.publicUrl ?? endpoint.url ?? `${endpoint.host}:${endpoint.port}`;
|
|
14904
|
+
}
|
|
14905
|
+
|
|
14805
14906
|
// packages/tui/src/fleet-controller.ts
|
|
14806
14907
|
function createFleetUiState() {
|
|
14807
14908
|
return {
|
|
@@ -14839,7 +14940,7 @@ function reconcileFleetSelection(previous, snapshot, preferredProject) {
|
|
|
14839
14940
|
const previousIndex = snapshot.stacks.findIndex((stack2) => stack2.project === previous.project);
|
|
14840
14941
|
const stack = snapshot.stacks.find((candidate) => candidate.project === previous.project) ?? snapshot.stacks.find((candidate) => candidate.project === preferredProject) ?? nearestItem(snapshot.stacks, previousIndex < 0 ? 0 : previousIndex) ?? snapshot.stacks[0];
|
|
14841
14942
|
const service = stack.services.find((candidate) => candidate.name === previous.service) ?? stack.services[0];
|
|
14842
|
-
const endpoints = service
|
|
14943
|
+
const endpoints = service === undefined ? [] : serviceEndpoints(service);
|
|
14843
14944
|
const endpoint = endpoints.find((candidate) => candidate.name === previous.endpoint);
|
|
14844
14945
|
return { project: stack.project, service: service?.name, endpoint: endpoint?.name };
|
|
14845
14946
|
}
|
|
@@ -14927,7 +15028,7 @@ function reduceFleetUiState(state, action) {
|
|
|
14927
15028
|
case "select-endpoint": {
|
|
14928
15029
|
const stack = action.snapshot.stacks.find((candidate) => candidate.project === state.selection.project);
|
|
14929
15030
|
const service = stack?.services.find((candidate) => candidate.name === action.service);
|
|
14930
|
-
if (!service
|
|
15031
|
+
if (service === undefined || !serviceEndpoints(service).some((endpoint) => endpoint.name === action.endpoint))
|
|
14931
15032
|
return state;
|
|
14932
15033
|
return {
|
|
14933
15034
|
...state,
|
|
@@ -14959,12 +15060,10 @@ function reduceFleetUiState(state, action) {
|
|
|
14959
15060
|
logOffset: action.follow ? 0 : state.logOffset,
|
|
14960
15061
|
unseenLines: action.follow ? 0 : state.unseenLines
|
|
14961
15062
|
};
|
|
14962
|
-
case "scroll-logs":
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
logOffset: Math.max(0, state.logOffset - action.delta)
|
|
14967
|
-
};
|
|
15063
|
+
case "scroll-logs": {
|
|
15064
|
+
const offset = Math.min(action.maxOffset ?? Number.MAX_SAFE_INTEGER, Math.max(0, state.logOffset - action.delta));
|
|
15065
|
+
return action.delta > 0 && offset === 0 ? { ...state, follow: true, logOffset: 0, unseenLines: 0 } : { ...state, follow: action.delta > 0 ? state.follow : false, logOffset: offset };
|
|
15066
|
+
}
|
|
14968
15067
|
case "new-lines":
|
|
14969
15068
|
return state.follow ? state : {
|
|
14970
15069
|
...state,
|
|
@@ -14992,11 +15091,24 @@ function reduceFleetUiState(state, action) {
|
|
|
14992
15091
|
return { ...state, downPending: action.pending };
|
|
14993
15092
|
}
|
|
14994
15093
|
}
|
|
15094
|
+
var init_fleet_controller = () => {};
|
|
14995
15095
|
|
|
14996
15096
|
// packages/tui/src/terminal-text.ts
|
|
15097
|
+
function collapseCarriageReturns(text) {
|
|
15098
|
+
if (!text.includes("\r"))
|
|
15099
|
+
return text;
|
|
15100
|
+
return text.split(`
|
|
15101
|
+
`).map((line) => {
|
|
15102
|
+
const parts = line.replace(/\r+$/, "").split("\r");
|
|
15103
|
+
return parts[parts.length - 1] ?? "";
|
|
15104
|
+
}).join(`
|
|
15105
|
+
`);
|
|
15106
|
+
}
|
|
14997
15107
|
function sanitizeFleetTerminalText(text, preserveNewlines = false) {
|
|
15108
|
+
const withoutEscapes = text.replace(ESCAPE_CONTROL_SEQUENCE, "").replace(C1_CONTROL_SEQUENCE, "");
|
|
15109
|
+
const collapsed = collapseCarriageReturns(withoutEscapes);
|
|
14998
15110
|
const controls = preserveNewlines ? /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g : /[\x00-\x1f\x7f-\x9f]/g;
|
|
14999
|
-
return
|
|
15111
|
+
return collapsed.replace(controls, "");
|
|
15000
15112
|
}
|
|
15001
15113
|
var ESCAPE_CONTROL_SEQUENCE, C1_CONTROL_SEQUENCE;
|
|
15002
15114
|
var init_terminal_text = __esm(() => {
|
|
@@ -15004,24 +15116,6 @@ var init_terminal_text = __esm(() => {
|
|
|
15004
15116
|
C1_CONTROL_SEQUENCE = /(?:[\x90\x98\x9d\x9e\x9f][\s\S]*?(?:\x07|\x9c)|\x9b[0-?]*[ -/]*[@-~])/g;
|
|
15005
15117
|
});
|
|
15006
15118
|
|
|
15007
|
-
// packages/tui/src/fleet-theme.ts
|
|
15008
|
-
var fleetTheme;
|
|
15009
|
-
var init_fleet_theme = __esm(() => {
|
|
15010
|
-
fleetTheme = {
|
|
15011
|
-
background: "#0d1117",
|
|
15012
|
-
panel: "#161b22",
|
|
15013
|
-
panelAlt: "#21262d",
|
|
15014
|
-
border: "#30363d",
|
|
15015
|
-
text: "#c9d1d9",
|
|
15016
|
-
muted: "#8b949e",
|
|
15017
|
-
accent: "#58a6ff",
|
|
15018
|
-
healthy: "#3fb950",
|
|
15019
|
-
warning: "#d29922",
|
|
15020
|
-
danger: "#f85149",
|
|
15021
|
-
selected: "#1f6feb"
|
|
15022
|
-
};
|
|
15023
|
-
});
|
|
15024
|
-
|
|
15025
15119
|
// node_modules/.bun/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
15026
15120
|
function ansiRegex({ onlyFirst = false } = {}) {
|
|
15027
15121
|
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
@@ -15204,229 +15298,623 @@ function padFleetText(text, width) {
|
|
|
15204
15298
|
const fitted = fitFleetText(text, width);
|
|
15205
15299
|
return fitted + " ".repeat(Math.max(0, width - stringWidth(fitted)));
|
|
15206
15300
|
}
|
|
15301
|
+
function wrapFleetText(text, width, maxLines = 3) {
|
|
15302
|
+
if (width <= 0)
|
|
15303
|
+
return [];
|
|
15304
|
+
if (text === "")
|
|
15305
|
+
return [""];
|
|
15306
|
+
if (stringWidth(text) <= width)
|
|
15307
|
+
return [text];
|
|
15308
|
+
const characters = Array.from(text);
|
|
15309
|
+
const lines = [];
|
|
15310
|
+
let index = 0;
|
|
15311
|
+
while (index < characters.length && lines.length < maxLines) {
|
|
15312
|
+
const last = lines.length === maxLines - 1;
|
|
15313
|
+
if (last) {
|
|
15314
|
+
lines.push(fitFleetText(characters.slice(index).join(""), width));
|
|
15315
|
+
break;
|
|
15316
|
+
}
|
|
15317
|
+
let line = "";
|
|
15318
|
+
let taken = 0;
|
|
15319
|
+
while (index + taken < characters.length) {
|
|
15320
|
+
const next = characters[index + taken];
|
|
15321
|
+
if (stringWidth(line + next) > width)
|
|
15322
|
+
break;
|
|
15323
|
+
line += next;
|
|
15324
|
+
taken += 1;
|
|
15325
|
+
}
|
|
15326
|
+
if (taken === 0) {
|
|
15327
|
+
lines.push(characters[index]);
|
|
15328
|
+
index += 1;
|
|
15329
|
+
continue;
|
|
15330
|
+
}
|
|
15331
|
+
lines.push(line);
|
|
15332
|
+
index += taken;
|
|
15333
|
+
}
|
|
15334
|
+
return lines;
|
|
15335
|
+
}
|
|
15207
15336
|
var init_fleet_text = __esm(() => {
|
|
15208
15337
|
init_string_width();
|
|
15209
15338
|
});
|
|
15210
15339
|
|
|
15211
|
-
// packages/tui/src/
|
|
15212
|
-
|
|
15213
|
-
|
|
15340
|
+
// packages/tui/src/fleet-theme.ts
|
|
15341
|
+
function blendHex(fg, bg, amount) {
|
|
15342
|
+
const parse = (hex2) => [
|
|
15343
|
+
Number.parseInt(hex2.slice(1, 3), 16),
|
|
15344
|
+
Number.parseInt(hex2.slice(3, 5), 16),
|
|
15345
|
+
Number.parseInt(hex2.slice(5, 7), 16)
|
|
15346
|
+
];
|
|
15347
|
+
const [fr, fg2, fb] = parse(fg);
|
|
15348
|
+
const [br, bg2, bb] = parse(bg);
|
|
15349
|
+
const channel = (f, b) => Math.round(b + (f - b) * amount);
|
|
15350
|
+
const hex = (value) => value.toString(16).padStart(2, "0");
|
|
15351
|
+
return `#${hex(channel(fr, br))}${hex(channel(fg2, bg2))}${hex(channel(fb, bb))}`;
|
|
15352
|
+
}
|
|
15353
|
+
function backendColor(backend) {
|
|
15354
|
+
if (backend === "docker")
|
|
15355
|
+
return fleetTheme.backendDocker;
|
|
15356
|
+
if (backend === "tunnel")
|
|
15357
|
+
return fleetTheme.backendTunnel;
|
|
15358
|
+
if (backend === "wrangler" || backend === "proc")
|
|
15359
|
+
return fleetTheme.backendProc;
|
|
15360
|
+
return fleetTheme.muted;
|
|
15361
|
+
}
|
|
15362
|
+
function serviceStateColor(state) {
|
|
15363
|
+
if (state === "healthy")
|
|
15364
|
+
return fleetTheme.healthy;
|
|
15365
|
+
if (state === "unknown" || state === "unhealthy" || state === "starting")
|
|
15366
|
+
return fleetTheme.warning;
|
|
15367
|
+
return fleetTheme.danger;
|
|
15368
|
+
}
|
|
15369
|
+
function serviceStateGlyph(state) {
|
|
15370
|
+
if (state === "healthy")
|
|
15371
|
+
return "\u25CF";
|
|
15372
|
+
if (state === "unhealthy")
|
|
15373
|
+
return "\u25C6";
|
|
15374
|
+
if (state === "starting")
|
|
15375
|
+
return "\u25CC";
|
|
15376
|
+
return "\u25CB";
|
|
15377
|
+
}
|
|
15378
|
+
function stackPhaseColor(phase) {
|
|
15214
15379
|
if (phase === "up")
|
|
15215
15380
|
return fleetTheme.healthy;
|
|
15216
|
-
if (phase === "degraded" || phase === "unknown")
|
|
15381
|
+
if (phase === "degraded" || phase === "unknown" || phase === "starting")
|
|
15217
15382
|
return fleetTheme.warning;
|
|
15383
|
+
if (phase === "queued" || phase === "reserved")
|
|
15384
|
+
return fleetTheme.queued;
|
|
15218
15385
|
if (phase === "stopped")
|
|
15219
|
-
return fleetTheme.
|
|
15386
|
+
return fleetTheme.faint;
|
|
15220
15387
|
return fleetTheme.accent;
|
|
15221
15388
|
}
|
|
15222
|
-
function
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
fg: fleetTheme.text,
|
|
15235
|
-
children: "Fleet"
|
|
15236
|
-
}, undefined, false, undefined, this)
|
|
15237
|
-
}, undefined, false, undefined, this),
|
|
15238
|
-
stacks.length === 0 ? /* @__PURE__ */ jsxDEV("box", {
|
|
15239
|
-
style: { paddingLeft: 1, paddingTop: 1 },
|
|
15240
|
-
children: /* @__PURE__ */ jsxDEV("text", {
|
|
15241
|
-
fg: fleetTheme.muted,
|
|
15242
|
-
children: "No managed stacks"
|
|
15243
|
-
}, undefined, false, undefined, this)
|
|
15244
|
-
}, undefined, false, undefined, this) : stacks.map((stack) => {
|
|
15245
|
-
const selected = stack.project === selectedProject;
|
|
15246
|
-
const branch = sanitizeFleetTerminalText(stack.branch);
|
|
15247
|
-
return /* @__PURE__ */ jsxDEV("box", {
|
|
15248
|
-
onMouseDown: (event) => {
|
|
15249
|
-
if (event.button !== 0)
|
|
15250
|
-
return;
|
|
15251
|
-
event.preventDefault();
|
|
15252
|
-
event.stopPropagation();
|
|
15253
|
-
onSelectProject?.(stack.project);
|
|
15254
|
-
},
|
|
15255
|
-
style: {
|
|
15256
|
-
height: 1,
|
|
15257
|
-
paddingLeft: 1,
|
|
15258
|
-
paddingRight: 1,
|
|
15259
|
-
flexDirection: "row",
|
|
15260
|
-
backgroundColor: selected ? fleetTheme.selected : fleetTheme.background
|
|
15261
|
-
},
|
|
15262
|
-
children: [
|
|
15263
|
-
/* @__PURE__ */ jsxDEV("text", {
|
|
15264
|
-
fg: selected ? "#ffffff" : phaseColor(stack.phase),
|
|
15265
|
-
children: selected ? "\u25B8 " : " "
|
|
15266
|
-
}, undefined, false, undefined, this),
|
|
15267
|
-
/* @__PURE__ */ jsxDEV("text", {
|
|
15268
|
-
fg: selected ? "#ffffff" : fleetTheme.text,
|
|
15269
|
-
children: padFleetText(branch, Math.max(4, width - 13))
|
|
15270
|
-
}, undefined, false, undefined, this),
|
|
15271
|
-
/* @__PURE__ */ jsxDEV("text", {
|
|
15272
|
-
fg: selected ? "#ffffff" : phaseColor(stack.phase),
|
|
15273
|
-
children: padFleetText(stack.phase, 9)
|
|
15274
|
-
}, undefined, false, undefined, this)
|
|
15275
|
-
]
|
|
15276
|
-
}, stack.project, true, undefined, this);
|
|
15277
|
-
})
|
|
15278
|
-
]
|
|
15279
|
-
}, undefined, true, undefined, this);
|
|
15389
|
+
function stackPhaseGlyph(phase, spinnerFrame = 0) {
|
|
15390
|
+
if (phase === "up")
|
|
15391
|
+
return "\u25CF";
|
|
15392
|
+
if (phase === "degraded")
|
|
15393
|
+
return "\u25C6";
|
|
15394
|
+
if (phase === "starting")
|
|
15395
|
+
return SPINNER_FRAMES[spinnerFrame % SPINNER_FRAMES.length];
|
|
15396
|
+
if (phase === "queued" || phase === "reserved")
|
|
15397
|
+
return "\u25CC";
|
|
15398
|
+
if (phase === "stopped")
|
|
15399
|
+
return "\u25CB";
|
|
15400
|
+
return "\u25CB";
|
|
15280
15401
|
}
|
|
15281
|
-
var
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15402
|
+
var background = "#0d1117", text = "#c9d1d9", accent = "#58a6ff", fleetTheme, SPINNER_FRAMES;
|
|
15403
|
+
var init_fleet_theme = __esm(() => {
|
|
15404
|
+
fleetTheme = {
|
|
15405
|
+
background,
|
|
15406
|
+
panel: blendHex(text, background, 0.04),
|
|
15407
|
+
panelAlt: blendHex(text, background, 0.1),
|
|
15408
|
+
border: blendHex(text, background, 0.18),
|
|
15409
|
+
text,
|
|
15410
|
+
bright: "#e6edf3",
|
|
15411
|
+
muted: "#8b949e",
|
|
15412
|
+
faint: "#6e7681",
|
|
15413
|
+
accent,
|
|
15414
|
+
selectedBg: blendHex(accent, background, 0.16),
|
|
15415
|
+
stripe: accent,
|
|
15416
|
+
keycapBg: blendHex(text, background, 0.22),
|
|
15417
|
+
healthy: "#3fb950",
|
|
15418
|
+
warning: "#d29922",
|
|
15419
|
+
danger: "#f85149",
|
|
15420
|
+
queued: "#bc8cff",
|
|
15421
|
+
backendDocker: "#58a6ff",
|
|
15422
|
+
backendProc: "#bc8cff",
|
|
15423
|
+
backendWrangler: "#bc8cff",
|
|
15424
|
+
backendTunnel: "#ffa657",
|
|
15425
|
+
publicBadge: "#ffa657"
|
|
15426
|
+
};
|
|
15427
|
+
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
15285
15428
|
});
|
|
15286
15429
|
|
|
15287
|
-
// packages/tui/src/
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
return fleetTheme.healthy;
|
|
15292
|
-
if (service.state === "unknown" || service.state === "unhealthy")
|
|
15293
|
-
return fleetTheme.warning;
|
|
15294
|
-
return fleetTheme.danger;
|
|
15430
|
+
// packages/tui/src/fleet-keys.ts
|
|
15431
|
+
function isEscapeKey(key) {
|
|
15432
|
+
const name = key.name.toLowerCase();
|
|
15433
|
+
return name === "escape" || name === "esc" || key.sequence === "\x1B";
|
|
15295
15434
|
}
|
|
15296
|
-
function
|
|
15297
|
-
return
|
|
15435
|
+
function isEnterKey(key) {
|
|
15436
|
+
return key.name === "return" || key.name === "enter";
|
|
15298
15437
|
}
|
|
15299
|
-
function
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
return
|
|
15307
|
-
|
|
15308
|
-
|
|
15309
|
-
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15438
|
+
function isPlainKey(key, value) {
|
|
15439
|
+
return !key.ctrl && !key.meta && !key.option && !key.super && !key.hyper && (key.name === value || key.sequence === value);
|
|
15440
|
+
}
|
|
15441
|
+
function isUpKey(key) {
|
|
15442
|
+
return key.name === "up" || isPlainKey(key, "k");
|
|
15443
|
+
}
|
|
15444
|
+
function isDownKey(key) {
|
|
15445
|
+
return key.name === "down" || isPlainKey(key, "j");
|
|
15446
|
+
}
|
|
15447
|
+
function isShiftedKey(key, lower) {
|
|
15448
|
+
return key.sequence === lower.toUpperCase() || isPlainKey(key, lower) && key.shift === true;
|
|
15449
|
+
}
|
|
15450
|
+
function isFollowBottomKey(key) {
|
|
15451
|
+
return isShiftedKey(key, "g");
|
|
15452
|
+
}
|
|
15453
|
+
function isScrollTopKey(key) {
|
|
15454
|
+
return !isShiftedKey(key, "g") && (key.sequence === "g" || isPlainKey(key, "g"));
|
|
15455
|
+
}
|
|
15456
|
+
function isDoctorKey(key) {
|
|
15457
|
+
return isShiftedKey(key, "d");
|
|
15458
|
+
}
|
|
15459
|
+
|
|
15460
|
+
// packages/tui/src/fleet-layout.ts
|
|
15461
|
+
function resolveFleetLayout(mode, terminalWidth) {
|
|
15462
|
+
if (mode === "auto")
|
|
15463
|
+
return terminalWidth >= SPLIT_MIN_WIDTH ? "split" : "stack";
|
|
15464
|
+
return mode;
|
|
15465
|
+
}
|
|
15466
|
+
function fleetPaneWidths(layout, terminalWidth) {
|
|
15467
|
+
if (layout === "stack")
|
|
15468
|
+
return { sidebar: terminalWidth, main: terminalWidth };
|
|
15469
|
+
const sidebar = Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, Math.floor(terminalWidth * 0.26)));
|
|
15470
|
+
return { sidebar, main: Math.max(20, terminalWidth - sidebar) };
|
|
15471
|
+
}
|
|
15472
|
+
function servicePaneHeight(rows, maxHeight) {
|
|
15473
|
+
const content = Math.max(2, rows + 1);
|
|
15474
|
+
return Math.min(maxHeight, content + 3);
|
|
15475
|
+
}
|
|
15476
|
+
function stackSidebarHeight(stackCount) {
|
|
15477
|
+
return Math.min(9, Math.max(4, stackCount + 3));
|
|
15478
|
+
}
|
|
15479
|
+
var SPLIT_MIN_WIDTH = 110, SIDEBAR_MIN_WIDTH = 28, SIDEBAR_MAX_WIDTH = 44;
|
|
15480
|
+
|
|
15481
|
+
// packages/tui/src/fleet-log-rows.ts
|
|
15482
|
+
function lineId(line) {
|
|
15483
|
+
let id = lineIds.get(line);
|
|
15484
|
+
if (id === undefined) {
|
|
15485
|
+
id = nextLineId++;
|
|
15486
|
+
lineIds.set(line, id);
|
|
15487
|
+
}
|
|
15488
|
+
return id;
|
|
15489
|
+
}
|
|
15490
|
+
function wrappedBody(line, bodyWidth) {
|
|
15491
|
+
const cached = wrapCache.get(line);
|
|
15492
|
+
if (cached !== undefined && cached.width === bodyWidth)
|
|
15493
|
+
return cached.wrapped;
|
|
15494
|
+
const wrapped = wrapFleetText(line.text, bodyWidth, 3);
|
|
15495
|
+
wrapCache.set(line, { width: bodyWidth, wrapped });
|
|
15496
|
+
return wrapped;
|
|
15497
|
+
}
|
|
15498
|
+
function buildFleetLogRows(lines, contentWidth) {
|
|
15499
|
+
const rows = [];
|
|
15500
|
+
for (const line of lines) {
|
|
15501
|
+
const meta = line.meta === true;
|
|
15502
|
+
const bodyWidth = meta ? Math.max(1, contentWidth - META_TAG.length) : contentWidth;
|
|
15503
|
+
const wrapped = wrappedBody(line, bodyWidth);
|
|
15504
|
+
const id = lineId(line);
|
|
15505
|
+
for (let part = 0;part < wrapped.length; part += 1) {
|
|
15506
|
+
rows.push({
|
|
15507
|
+
key: `${id}:${part}`,
|
|
15508
|
+
text: wrapped[part],
|
|
15509
|
+
meta,
|
|
15510
|
+
tag: !meta ? undefined : part === 0 ? META_TAG : " ".repeat(META_TAG.length)
|
|
15511
|
+
});
|
|
15512
|
+
}
|
|
15513
|
+
}
|
|
15514
|
+
return rows;
|
|
15515
|
+
}
|
|
15516
|
+
var META_TAG = "hestia \u2502 ", nextLineId = 0, lineIds, wrapCache;
|
|
15517
|
+
var init_fleet_log_rows = __esm(() => {
|
|
15518
|
+
init_fleet_text();
|
|
15519
|
+
lineIds = new WeakMap;
|
|
15520
|
+
wrapCache = new WeakMap;
|
|
15521
|
+
});
|
|
15522
|
+
|
|
15523
|
+
// packages/tui/src/fleet-format.ts
|
|
15524
|
+
function formatUptime(createdAt, now) {
|
|
15525
|
+
const born = Date.parse(createdAt);
|
|
15526
|
+
if (!Number.isFinite(born))
|
|
15527
|
+
return;
|
|
15528
|
+
const seconds = Math.max(0, Math.floor((now - born) / 1000));
|
|
15529
|
+
if (seconds < 60)
|
|
15530
|
+
return `${seconds}s`;
|
|
15531
|
+
const minutes = Math.floor(seconds / 60);
|
|
15532
|
+
if (minutes < 60)
|
|
15533
|
+
return `${minutes}m`;
|
|
15534
|
+
const hours = Math.floor(minutes / 60);
|
|
15535
|
+
if (hours < 24)
|
|
15536
|
+
return `${hours}h${minutes % 60 === 0 ? "" : `${minutes % 60}m`}`;
|
|
15537
|
+
const days = Math.floor(hours / 24);
|
|
15538
|
+
return `${days}d${hours % 24 === 0 ? "" : `${hours % 24}h`}`;
|
|
15539
|
+
}
|
|
15540
|
+
function buildEnvBlock(stack) {
|
|
15541
|
+
const lines = [];
|
|
15542
|
+
for (const service of stack.services) {
|
|
15543
|
+
if (service.backend === "tunnel")
|
|
15544
|
+
continue;
|
|
15545
|
+
if (service.publishedPort !== undefined) {
|
|
15546
|
+
lines.push(`HESTIA_${envKey(service.name)}_PORT=${service.publishedPort}`);
|
|
15547
|
+
if (service.backend === "docker") {
|
|
15548
|
+
lines.push(`HESTIA_${envKey(service.name)}_MAIN_TCP_PORT=${service.publishedPort}`);
|
|
15549
|
+
}
|
|
15550
|
+
}
|
|
15551
|
+
for (const endpoint of serviceEndpoints(service)) {
|
|
15552
|
+
const alias = envKey(endpoint.name);
|
|
15553
|
+
lines.push(`HESTIA_${alias}_PORT=${endpoint.port}`);
|
|
15554
|
+
if (endpoint.publicUrl !== undefined)
|
|
15555
|
+
lines.push(`HESTIA_${alias}_URL=${endpoint.publicUrl}`);
|
|
15556
|
+
if (endpoint.localUrl !== undefined)
|
|
15557
|
+
lines.push(`HESTIA_${alias}_LOCAL_URL=${endpoint.localUrl}`);
|
|
15558
|
+
if (endpoint.url !== undefined)
|
|
15559
|
+
lines.push(`HESTIA_${alias}_DIRECT_URL=${endpoint.url}`);
|
|
15560
|
+
}
|
|
15561
|
+
}
|
|
15562
|
+
return [...new Set(lines)].join(`
|
|
15563
|
+
`);
|
|
15564
|
+
}
|
|
15565
|
+
var init_fleet_format = __esm(() => {
|
|
15566
|
+
init_src2();
|
|
15567
|
+
});
|
|
15568
|
+
|
|
15569
|
+
// packages/tui/src/fleet-status.ts
|
|
15570
|
+
function fleetCapacitySummary(capacity, connected) {
|
|
15571
|
+
if (!connected)
|
|
15572
|
+
return "daemon unreachable";
|
|
15573
|
+
const parts = [`${capacity.live}/${capacity.maxStacks}`];
|
|
15574
|
+
if (capacity.reserved > 0)
|
|
15575
|
+
parts.push(`${capacity.reserved} reserved`);
|
|
15576
|
+
if (capacity.queued > 0)
|
|
15577
|
+
parts.push(`${capacity.queued} queued`);
|
|
15578
|
+
parts.push("daemon ok");
|
|
15579
|
+
return parts.join(" \xB7 ");
|
|
15580
|
+
}
|
|
15581
|
+
function resolveStatusNotice(connectionNotice, toast) {
|
|
15582
|
+
return toast ?? connectionNotice;
|
|
15583
|
+
}
|
|
15584
|
+
var FLEET_KEY_HINTS;
|
|
15585
|
+
var init_fleet_status = __esm(() => {
|
|
15586
|
+
FLEET_KEY_HINTS = [
|
|
15587
|
+
{ keys: ", .", label: "stack" },
|
|
15588
|
+
{ keys: "[ ]", label: "svc" },
|
|
15589
|
+
{ keys: "o", label: "open" },
|
|
15590
|
+
{ keys: "y", label: "yank" },
|
|
15591
|
+
{ keys: "?", label: "help" }
|
|
15592
|
+
];
|
|
15593
|
+
});
|
|
15594
|
+
|
|
15595
|
+
// packages/tui/src/components/StackSidebar.tsx
|
|
15596
|
+
import { memo } from "react";
|
|
15597
|
+
import { jsxDEV } from "@opentui/react/jsx-dev-runtime";
|
|
15598
|
+
var StackSidebar;
|
|
15599
|
+
var init_StackSidebar = __esm(() => {
|
|
15600
|
+
init_string_width();
|
|
15601
|
+
init_fleet_text();
|
|
15602
|
+
init_fleet_theme();
|
|
15603
|
+
init_terminal_text();
|
|
15604
|
+
StackSidebar = memo(function StackSidebar2({
|
|
15605
|
+
stacks,
|
|
15606
|
+
capacity,
|
|
15607
|
+
selectedProject,
|
|
15608
|
+
width,
|
|
15609
|
+
focused = false,
|
|
15610
|
+
spinnerFrame = 0,
|
|
15611
|
+
onSelectProject
|
|
15612
|
+
}) {
|
|
15613
|
+
const branchWidth = Math.max(6, width - 17);
|
|
15614
|
+
const slots = capacity.maxStacks > 0 ? ` \xB7 ${capacity.live}/${capacity.maxStacks} slots` : "";
|
|
15615
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
15616
|
+
style: {
|
|
15617
|
+
width,
|
|
15618
|
+
height: "100%",
|
|
15619
|
+
flexDirection: "column",
|
|
15620
|
+
border: true,
|
|
15621
|
+
borderColor: focused ? fleetTheme.accent : fleetTheme.border,
|
|
15622
|
+
backgroundColor: fleetTheme.background
|
|
15623
|
+
},
|
|
15624
|
+
children: [
|
|
15625
|
+
/* @__PURE__ */ jsxDEV("box", {
|
|
15626
|
+
style: { height: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "row", backgroundColor: fleetTheme.panelAlt },
|
|
15334
15627
|
children: [
|
|
15335
|
-
/* @__PURE__ */
|
|
15336
|
-
fg:
|
|
15337
|
-
children:
|
|
15338
|
-
}, undefined, false, undefined, this),
|
|
15339
|
-
/* @__PURE__ */ jsxDEV2("text", {
|
|
15340
|
-
fg: workloadSelected ? "#ffffff" : fleetTheme.text,
|
|
15341
|
-
children: padFleetText(sanitizeFleetTerminalText(service.name), 20)
|
|
15628
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15629
|
+
fg: focused ? fleetTheme.accent : fleetTheme.text,
|
|
15630
|
+
children: "Stacks"
|
|
15342
15631
|
}, undefined, false, undefined, this),
|
|
15343
|
-
/* @__PURE__ */
|
|
15344
|
-
fg:
|
|
15345
|
-
children:
|
|
15632
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15633
|
+
fg: fleetTheme.faint,
|
|
15634
|
+
children: slots
|
|
15635
|
+
}, undefined, false, undefined, this)
|
|
15636
|
+
]
|
|
15637
|
+
}, undefined, true, undefined, this),
|
|
15638
|
+
stacks.length === 0 ? /* @__PURE__ */ jsxDEV("box", {
|
|
15639
|
+
style: { paddingLeft: 1, paddingTop: 1, flexDirection: "column" },
|
|
15640
|
+
children: [
|
|
15641
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15642
|
+
fg: fleetTheme.muted,
|
|
15643
|
+
children: "No managed stacks."
|
|
15346
15644
|
}, undefined, false, undefined, this),
|
|
15347
|
-
/* @__PURE__ */
|
|
15348
|
-
fg:
|
|
15349
|
-
children:
|
|
15645
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15646
|
+
fg: fleetTheme.faint,
|
|
15647
|
+
children: "`hestia up` starts one here."
|
|
15350
15648
|
}, undefined, false, undefined, this)
|
|
15351
15649
|
]
|
|
15352
|
-
},
|
|
15353
|
-
|
|
15354
|
-
const
|
|
15355
|
-
const
|
|
15356
|
-
const
|
|
15357
|
-
const
|
|
15358
|
-
|
|
15650
|
+
}, undefined, true, undefined, this) : stacks.map((stack) => {
|
|
15651
|
+
const selected = stack.project === selectedProject;
|
|
15652
|
+
const branch = sanitizeFleetTerminalText(stack.branch);
|
|
15653
|
+
const phase = sanitizeFleetTerminalText(stack.phase);
|
|
15654
|
+
const glyph = stackPhaseGlyph(stack.phase, spinnerFrame);
|
|
15655
|
+
const meta = stack.services.length > 0 ? ` \xB7${stack.services.length}` : "";
|
|
15656
|
+
const warn = stack.warning === undefined ? "" : " \u26A0";
|
|
15657
|
+
const available = Math.max(4, branchWidth - stringWidth(meta) - stringWidth(warn));
|
|
15658
|
+
const rowBg = selected ? fleetTheme.selectedBg : fleetTheme.background;
|
|
15659
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
15359
15660
|
onMouseDown: (event) => {
|
|
15360
15661
|
if (event.button !== 0)
|
|
15361
15662
|
return;
|
|
15362
15663
|
event.preventDefault();
|
|
15363
15664
|
event.stopPropagation();
|
|
15364
|
-
|
|
15365
|
-
},
|
|
15366
|
-
style: {
|
|
15367
|
-
height: 1,
|
|
15368
|
-
paddingLeft: 2,
|
|
15369
|
-
paddingRight: 1,
|
|
15370
|
-
flexDirection: "row",
|
|
15371
|
-
backgroundColor: endpointSelected ? fleetTheme.selected : fleetTheme.background
|
|
15665
|
+
onSelectProject?.(stack.project);
|
|
15372
15666
|
},
|
|
15667
|
+
style: { height: 1, paddingRight: 1, flexDirection: "row", backgroundColor: rowBg },
|
|
15373
15668
|
children: [
|
|
15374
|
-
/* @__PURE__ */
|
|
15375
|
-
fg:
|
|
15376
|
-
children:
|
|
15669
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15670
|
+
fg: fleetTheme.stripe,
|
|
15671
|
+
children: selected ? "\u258E" : " "
|
|
15377
15672
|
}, undefined, false, undefined, this),
|
|
15378
|
-
/* @__PURE__ */
|
|
15379
|
-
fg:
|
|
15380
|
-
children:
|
|
15673
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15674
|
+
fg: stackPhaseColor(stack.phase),
|
|
15675
|
+
children: [
|
|
15676
|
+
glyph,
|
|
15677
|
+
" "
|
|
15678
|
+
]
|
|
15679
|
+
}, undefined, true, undefined, this),
|
|
15680
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15681
|
+
fg: selected ? fleetTheme.bright : fleetTheme.text,
|
|
15682
|
+
children: padFleetText(fitFleetText(branch, available), available)
|
|
15381
15683
|
}, undefined, false, undefined, this),
|
|
15382
|
-
/* @__PURE__ */
|
|
15383
|
-
fg:
|
|
15384
|
-
children:
|
|
15684
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15685
|
+
fg: fleetTheme.warning,
|
|
15686
|
+
children: warn
|
|
15385
15687
|
}, undefined, false, undefined, this),
|
|
15386
|
-
/* @__PURE__ */
|
|
15387
|
-
fg:
|
|
15388
|
-
children:
|
|
15688
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15689
|
+
fg: fleetTheme.faint,
|
|
15690
|
+
children: meta
|
|
15389
15691
|
}, undefined, false, undefined, this),
|
|
15390
|
-
/* @__PURE__ */
|
|
15391
|
-
fg:
|
|
15392
|
-
children:
|
|
15692
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
15693
|
+
fg: stackPhaseColor(stack.phase),
|
|
15694
|
+
children: ` ${padFleetText(phase, 8)}`
|
|
15393
15695
|
}, undefined, false, undefined, this)
|
|
15394
15696
|
]
|
|
15395
|
-
},
|
|
15396
|
-
})
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15697
|
+
}, stack.project, true, undefined, this);
|
|
15698
|
+
})
|
|
15699
|
+
]
|
|
15700
|
+
}, undefined, true, undefined, this);
|
|
15701
|
+
});
|
|
15702
|
+
});
|
|
15703
|
+
|
|
15704
|
+
// packages/tui/src/components/ServicePane.tsx
|
|
15705
|
+
import { memo as memo2 } from "react";
|
|
15706
|
+
import { jsxDEV as jsxDEV2, Fragment } from "@opentui/react/jsx-dev-runtime";
|
|
15707
|
+
function portLabel(service) {
|
|
15708
|
+
if (service.publishedPort === undefined)
|
|
15709
|
+
return "\u2014";
|
|
15710
|
+
return `:${service.publishedPort}`;
|
|
15711
|
+
}
|
|
15712
|
+
var ServicePane;
|
|
15408
15713
|
var init_ServicePane = __esm(() => {
|
|
15409
15714
|
init_fleet_text();
|
|
15410
15715
|
init_fleet_theme();
|
|
15411
15716
|
init_terminal_text();
|
|
15717
|
+
ServicePane = memo2(function ServicePane2({
|
|
15718
|
+
stack,
|
|
15719
|
+
uptime,
|
|
15720
|
+
selectedService,
|
|
15721
|
+
selectedEndpoint,
|
|
15722
|
+
width = 80,
|
|
15723
|
+
focused = false,
|
|
15724
|
+
onSelectService,
|
|
15725
|
+
onSelectEndpoint
|
|
15726
|
+
}) {
|
|
15727
|
+
const nameWidth = Math.min(24, Math.max(12, Math.floor(width * 0.28)));
|
|
15728
|
+
const backendWidth = 10;
|
|
15729
|
+
const stateWidth = 10;
|
|
15730
|
+
const portWidth = 7;
|
|
15731
|
+
const title = stack === undefined ? "Workloads" : `Workloads \u2014 ${sanitizeFleetTerminalText(stack.branch)}`;
|
|
15732
|
+
const warning = stack?.warning === undefined ? undefined : sanitizeFleetTerminalText(stack.warning);
|
|
15733
|
+
return /* @__PURE__ */ jsxDEV2("box", {
|
|
15734
|
+
style: {
|
|
15735
|
+
height: "100%",
|
|
15736
|
+
width: "100%",
|
|
15737
|
+
flexDirection: "column",
|
|
15738
|
+
border: true,
|
|
15739
|
+
borderColor: focused ? fleetTheme.accent : fleetTheme.border,
|
|
15740
|
+
backgroundColor: fleetTheme.background
|
|
15741
|
+
},
|
|
15742
|
+
children: [
|
|
15743
|
+
/* @__PURE__ */ jsxDEV2("box", {
|
|
15744
|
+
style: { height: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "row", backgroundColor: fleetTheme.panelAlt },
|
|
15745
|
+
children: [
|
|
15746
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15747
|
+
fg: focused ? fleetTheme.accent : fleetTheme.text,
|
|
15748
|
+
children: fitFleetText(title, Math.max(8, width - 14))
|
|
15749
|
+
}, undefined, false, undefined, this),
|
|
15750
|
+
uptime === undefined ? null : /* @__PURE__ */ jsxDEV2("text", {
|
|
15751
|
+
fg: fleetTheme.faint,
|
|
15752
|
+
children: [
|
|
15753
|
+
" \xB7 up ",
|
|
15754
|
+
uptime
|
|
15755
|
+
]
|
|
15756
|
+
}, undefined, true, undefined, this)
|
|
15757
|
+
]
|
|
15758
|
+
}, undefined, true, undefined, this),
|
|
15759
|
+
warning === undefined ? null : /* @__PURE__ */ jsxDEV2("box", {
|
|
15760
|
+
style: { height: 1, paddingLeft: 1, paddingRight: 1 },
|
|
15761
|
+
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
15762
|
+
fg: fleetTheme.warning,
|
|
15763
|
+
children: fitFleetText(`\u26A0 ${warning}`, Math.max(8, width - 4))
|
|
15764
|
+
}, undefined, false, undefined, this)
|
|
15765
|
+
}, undefined, false, undefined, this),
|
|
15766
|
+
stack?.services.length ? /* @__PURE__ */ jsxDEV2(Fragment, {
|
|
15767
|
+
children: [
|
|
15768
|
+
/* @__PURE__ */ jsxDEV2("box", {
|
|
15769
|
+
style: { height: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "row" },
|
|
15770
|
+
children: [
|
|
15771
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15772
|
+
fg: fleetTheme.faint,
|
|
15773
|
+
children: padFleetText("", 4)
|
|
15774
|
+
}, undefined, false, undefined, this),
|
|
15775
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15776
|
+
fg: fleetTheme.faint,
|
|
15777
|
+
children: padFleetText("NAME", nameWidth)
|
|
15778
|
+
}, undefined, false, undefined, this),
|
|
15779
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15780
|
+
fg: fleetTheme.faint,
|
|
15781
|
+
children: padFleetText("BACKEND", backendWidth)
|
|
15782
|
+
}, undefined, false, undefined, this),
|
|
15783
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15784
|
+
fg: fleetTheme.faint,
|
|
15785
|
+
children: padFleetText("STATE", stateWidth)
|
|
15786
|
+
}, undefined, false, undefined, this),
|
|
15787
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15788
|
+
fg: fleetTheme.faint,
|
|
15789
|
+
children: padFleetText("PORT", portWidth)
|
|
15790
|
+
}, undefined, false, undefined, this)
|
|
15791
|
+
]
|
|
15792
|
+
}, undefined, true, undefined, this),
|
|
15793
|
+
stack.services.flatMap((service) => {
|
|
15794
|
+
const workloadSelected = service.name === selectedService && selectedEndpoint === undefined;
|
|
15795
|
+
const endpoints = serviceEndpoints(service);
|
|
15796
|
+
const state = sanitizeFleetTerminalText(service.state);
|
|
15797
|
+
const workloadBg = workloadSelected ? fleetTheme.selectedBg : fleetTheme.background;
|
|
15798
|
+
const workloadRow = /* @__PURE__ */ jsxDEV2("box", {
|
|
15799
|
+
onMouseDown: (event) => {
|
|
15800
|
+
if (event.button !== 0)
|
|
15801
|
+
return;
|
|
15802
|
+
event.preventDefault();
|
|
15803
|
+
event.stopPropagation();
|
|
15804
|
+
onSelectService?.(service.name);
|
|
15805
|
+
},
|
|
15806
|
+
style: { height: 1, paddingRight: 1, flexDirection: "row", backgroundColor: workloadBg },
|
|
15807
|
+
children: [
|
|
15808
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15809
|
+
fg: fleetTheme.stripe,
|
|
15810
|
+
children: workloadSelected ? "\u258E" : " "
|
|
15811
|
+
}, undefined, false, undefined, this),
|
|
15812
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15813
|
+
fg: serviceStateColor(service.state),
|
|
15814
|
+
children: [
|
|
15815
|
+
" ",
|
|
15816
|
+
serviceStateGlyph(service.state),
|
|
15817
|
+
" "
|
|
15818
|
+
]
|
|
15819
|
+
}, undefined, true, undefined, this),
|
|
15820
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15821
|
+
fg: workloadSelected ? fleetTheme.bright : fleetTheme.text,
|
|
15822
|
+
children: padFleetText(sanitizeFleetTerminalText(service.name), nameWidth)
|
|
15823
|
+
}, undefined, false, undefined, this),
|
|
15824
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15825
|
+
fg: backendColor(service.backend),
|
|
15826
|
+
children: padFleetText(service.backend, backendWidth)
|
|
15827
|
+
}, undefined, false, undefined, this),
|
|
15828
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15829
|
+
fg: serviceStateColor(service.state),
|
|
15830
|
+
children: padFleetText(state, stateWidth)
|
|
15831
|
+
}, undefined, false, undefined, this),
|
|
15832
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15833
|
+
fg: fleetTheme.muted,
|
|
15834
|
+
children: padFleetText(portLabel(service), portWidth)
|
|
15835
|
+
}, undefined, false, undefined, this)
|
|
15836
|
+
]
|
|
15837
|
+
}, `workload:${service.name}`, true, undefined, this);
|
|
15838
|
+
const endpointRows = endpoints.map((endpoint, index) => {
|
|
15839
|
+
const endpointSelected = service.name === selectedService && endpoint.name === selectedEndpoint;
|
|
15840
|
+
const connector = index === endpoints.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
15841
|
+
const kind = (endpoint.kind ?? "http").toUpperCase();
|
|
15842
|
+
const reach = sanitizeFleetTerminalText(endpointReach(endpoint));
|
|
15843
|
+
const aliasWidth = Math.min(18, Math.max(10, Math.floor(width * 0.22)));
|
|
15844
|
+
const pub = endpoint.publicUrl === undefined ? "" : " pub";
|
|
15845
|
+
const endpointBg = endpointSelected ? fleetTheme.selectedBg : fleetTheme.background;
|
|
15846
|
+
return /* @__PURE__ */ jsxDEV2("box", {
|
|
15847
|
+
onMouseDown: (event) => {
|
|
15848
|
+
if (event.button !== 0)
|
|
15849
|
+
return;
|
|
15850
|
+
event.preventDefault();
|
|
15851
|
+
event.stopPropagation();
|
|
15852
|
+
onSelectEndpoint?.(service.name, endpoint.name);
|
|
15853
|
+
},
|
|
15854
|
+
style: { height: 1, paddingRight: 1, flexDirection: "row", backgroundColor: endpointBg },
|
|
15855
|
+
children: [
|
|
15856
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15857
|
+
fg: fleetTheme.stripe,
|
|
15858
|
+
children: endpointSelected ? "\u258E" : " "
|
|
15859
|
+
}, undefined, false, undefined, this),
|
|
15860
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15861
|
+
fg: fleetTheme.faint,
|
|
15862
|
+
children: ` ${connector} `
|
|
15863
|
+
}, undefined, false, undefined, this),
|
|
15864
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15865
|
+
fg: endpointSelected ? fleetTheme.bright : fleetTheme.accent,
|
|
15866
|
+
children: padFleetText(sanitizeFleetTerminalText(endpoint.name), aliasWidth)
|
|
15867
|
+
}, undefined, false, undefined, this),
|
|
15868
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15869
|
+
fg: fleetTheme.muted,
|
|
15870
|
+
children: padFleetText(kind, 6)
|
|
15871
|
+
}, undefined, false, undefined, this),
|
|
15872
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15873
|
+
fg: fleetTheme.text,
|
|
15874
|
+
children: fitFleetText(reach, Math.max(12, width - aliasWidth - 19 - pub.length))
|
|
15875
|
+
}, undefined, false, undefined, this),
|
|
15876
|
+
/* @__PURE__ */ jsxDEV2("text", {
|
|
15877
|
+
fg: fleetTheme.publicBadge,
|
|
15878
|
+
children: pub
|
|
15879
|
+
}, undefined, false, undefined, this)
|
|
15880
|
+
]
|
|
15881
|
+
}, `endpoint:${service.name}:${endpoint.name}`, true, undefined, this);
|
|
15882
|
+
});
|
|
15883
|
+
return [workloadRow, ...endpointRows];
|
|
15884
|
+
})
|
|
15885
|
+
]
|
|
15886
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV2("box", {
|
|
15887
|
+
style: { paddingLeft: 1, paddingTop: 1 },
|
|
15888
|
+
children: /* @__PURE__ */ jsxDEV2("text", {
|
|
15889
|
+
fg: fleetTheme.muted,
|
|
15890
|
+
children: stack?.phase === "queued" || stack?.phase === "reserved" ? "Waiting for a machine slot \u2014 services appear once startup begins." : "No workloads recorded"
|
|
15891
|
+
}, undefined, false, undefined, this)
|
|
15892
|
+
}, undefined, false, undefined, this)
|
|
15893
|
+
]
|
|
15894
|
+
}, undefined, true, undefined, this);
|
|
15895
|
+
});
|
|
15412
15896
|
});
|
|
15413
15897
|
|
|
15414
15898
|
// packages/tui/src/components/LogPane.tsx
|
|
15415
15899
|
import { jsxDEV as jsxDEV3 } from "@opentui/react/jsx-dev-runtime";
|
|
15416
15900
|
function LogPane({
|
|
15417
|
-
|
|
15901
|
+
rows,
|
|
15418
15902
|
height,
|
|
15419
15903
|
width,
|
|
15420
15904
|
offset,
|
|
15421
15905
|
follow,
|
|
15422
15906
|
unseen,
|
|
15423
15907
|
label,
|
|
15908
|
+
focused = false,
|
|
15424
15909
|
onScroll
|
|
15425
15910
|
}) {
|
|
15426
15911
|
const viewportRows = Math.max(1, height - 3);
|
|
15427
|
-
const
|
|
15428
|
-
const
|
|
15429
|
-
const
|
|
15912
|
+
const maxOffset = Math.max(0, rows.length - viewportRows);
|
|
15913
|
+
const clamped = Math.min(offset, maxOffset);
|
|
15914
|
+
const end = Math.max(0, rows.length - clamped);
|
|
15915
|
+
const start = Math.max(0, end - viewportRows);
|
|
15916
|
+
const visible = rows.slice(start, end);
|
|
15917
|
+
const title = fitFleetText(`Logs \u2014 ${label}`, Math.max(8, width - 28));
|
|
15430
15918
|
return /* @__PURE__ */ jsxDEV3("box", {
|
|
15431
15919
|
onMouseScroll: (event) => {
|
|
15432
15920
|
if (event.button !== 4 && event.button !== 5)
|
|
@@ -15435,14 +15923,21 @@ function LogPane({
|
|
|
15435
15923
|
event.stopPropagation();
|
|
15436
15924
|
onScroll?.(event.button === 4 ? -3 : 3);
|
|
15437
15925
|
},
|
|
15438
|
-
style: {
|
|
15926
|
+
style: {
|
|
15927
|
+
height: "100%",
|
|
15928
|
+
width: "100%",
|
|
15929
|
+
flexDirection: "column",
|
|
15930
|
+
border: true,
|
|
15931
|
+
borderColor: focused ? fleetTheme.accent : fleetTheme.border,
|
|
15932
|
+
backgroundColor: fleetTheme.background
|
|
15933
|
+
},
|
|
15439
15934
|
children: [
|
|
15440
15935
|
/* @__PURE__ */ jsxDEV3("box", {
|
|
15441
15936
|
style: { height: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "row", backgroundColor: fleetTheme.panelAlt },
|
|
15442
15937
|
children: [
|
|
15443
15938
|
/* @__PURE__ */ jsxDEV3("text", {
|
|
15444
|
-
fg: fleetTheme.text,
|
|
15445
|
-
children: padFleetText(
|
|
15939
|
+
fg: focused ? fleetTheme.accent : fleetTheme.text,
|
|
15940
|
+
children: padFleetText(title, Math.max(1, width - 24))
|
|
15446
15941
|
}, undefined, false, undefined, this),
|
|
15447
15942
|
/* @__PURE__ */ jsxDEV3("text", {
|
|
15448
15943
|
fg: follow ? fleetTheme.healthy : fleetTheme.warning,
|
|
@@ -15454,15 +15949,21 @@ function LogPane({
|
|
|
15454
15949
|
style: { paddingLeft: 1, paddingTop: 1 },
|
|
15455
15950
|
children: /* @__PURE__ */ jsxDEV3("text", {
|
|
15456
15951
|
fg: fleetTheme.muted,
|
|
15457
|
-
children: "No log output yet \u2014
|
|
15952
|
+
children: "No log output yet \u2014 waiting for this workload."
|
|
15458
15953
|
}, undefined, false, undefined, this)
|
|
15459
|
-
}, undefined, false, undefined, this) : visible.map((
|
|
15460
|
-
style: { height: 1, paddingLeft: 1 },
|
|
15461
|
-
children:
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15954
|
+
}, undefined, false, undefined, this) : visible.map((row2) => /* @__PURE__ */ jsxDEV3("box", {
|
|
15955
|
+
style: { height: 1, paddingLeft: 1, flexDirection: "row" },
|
|
15956
|
+
children: [
|
|
15957
|
+
row2.tag === undefined ? null : /* @__PURE__ */ jsxDEV3("text", {
|
|
15958
|
+
fg: fleetTheme.accent,
|
|
15959
|
+
children: row2.tag
|
|
15960
|
+
}, undefined, false, undefined, this),
|
|
15961
|
+
/* @__PURE__ */ jsxDEV3("text", {
|
|
15962
|
+
fg: row2.meta ? fleetTheme.warning : fleetTheme.text,
|
|
15963
|
+
children: row2.text
|
|
15964
|
+
}, undefined, false, undefined, this)
|
|
15965
|
+
]
|
|
15966
|
+
}, row2.key, true, undefined, this))
|
|
15466
15967
|
]
|
|
15467
15968
|
}, undefined, true, undefined, this);
|
|
15468
15969
|
}
|
|
@@ -15472,16 +15973,16 @@ var init_LogPane = __esm(() => {
|
|
|
15472
15973
|
});
|
|
15473
15974
|
|
|
15474
15975
|
// packages/tui/src/components/FleetModal.tsx
|
|
15475
|
-
import { jsxDEV as jsxDEV4, Fragment } from "@opentui/react/jsx-dev-runtime";
|
|
15976
|
+
import { jsxDEV as jsxDEV4, Fragment as Fragment2 } from "@opentui/react/jsx-dev-runtime";
|
|
15476
15977
|
function FleetModal({
|
|
15477
15978
|
title,
|
|
15478
15979
|
terminalWidth,
|
|
15479
15980
|
terminalHeight,
|
|
15480
15981
|
children
|
|
15481
15982
|
}) {
|
|
15482
|
-
const width = Math.min(
|
|
15483
|
-
const height = Math.min(
|
|
15484
|
-
return /* @__PURE__ */ jsxDEV4(
|
|
15983
|
+
const width = Math.min(78, Math.max(36, terminalWidth - 4));
|
|
15984
|
+
const height = Math.min(22, Math.max(8, terminalHeight - 4));
|
|
15985
|
+
return /* @__PURE__ */ jsxDEV4(Fragment2, {
|
|
15485
15986
|
children: [
|
|
15486
15987
|
/* @__PURE__ */ jsxDEV4("box", {
|
|
15487
15988
|
style: { position: "absolute", top: 0, left: 0, width: terminalWidth, height: terminalHeight, zIndex: 50 }
|
|
@@ -15503,10 +16004,22 @@ function FleetModal({
|
|
|
15503
16004
|
paddingTop: 1
|
|
15504
16005
|
},
|
|
15505
16006
|
children: [
|
|
15506
|
-
/* @__PURE__ */ jsxDEV4("
|
|
15507
|
-
|
|
15508
|
-
children:
|
|
15509
|
-
|
|
16007
|
+
/* @__PURE__ */ jsxDEV4("box", {
|
|
16008
|
+
style: { height: 1, flexDirection: "row" },
|
|
16009
|
+
children: [
|
|
16010
|
+
/* @__PURE__ */ jsxDEV4("box", {
|
|
16011
|
+
style: { flexGrow: 1 },
|
|
16012
|
+
children: /* @__PURE__ */ jsxDEV4("text", {
|
|
16013
|
+
fg: fleetTheme.accent,
|
|
16014
|
+
children: title
|
|
16015
|
+
}, undefined, false, undefined, this)
|
|
16016
|
+
}, undefined, false, undefined, this),
|
|
16017
|
+
/* @__PURE__ */ jsxDEV4("text", {
|
|
16018
|
+
fg: fleetTheme.faint,
|
|
16019
|
+
children: "Esc closes"
|
|
16020
|
+
}, undefined, false, undefined, this)
|
|
16021
|
+
]
|
|
16022
|
+
}, undefined, true, undefined, this),
|
|
15510
16023
|
/* @__PURE__ */ jsxDEV4("box", {
|
|
15511
16024
|
style: { height: 1 }
|
|
15512
16025
|
}, undefined, false, undefined, this),
|
|
@@ -15521,10 +16034,10 @@ var init_FleetModal = __esm(() => {
|
|
|
15521
16034
|
});
|
|
15522
16035
|
|
|
15523
16036
|
// packages/tui/src/FleetApp.tsx
|
|
15524
|
-
import { useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
16037
|
+
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
15525
16038
|
import { existsSync as existsSync23 } from "fs";
|
|
15526
16039
|
import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/react";
|
|
15527
|
-
import { jsxDEV as jsxDEV5, Fragment as
|
|
16040
|
+
import { jsxDEV as jsxDEV5, Fragment as Fragment3 } from "@opentui/react/jsx-dev-runtime";
|
|
15528
16041
|
function middleTruncateWorktreePath(path, width) {
|
|
15529
16042
|
if (path.length <= width)
|
|
15530
16043
|
return path;
|
|
@@ -15542,32 +16055,24 @@ function middleTruncateWorktreePath(path, width) {
|
|
|
15542
16055
|
function emptySnapshot(repoId) {
|
|
15543
16056
|
return { repoId, observedAt: new Date(0).toISOString(), capacity: EMPTY_CAPACITY, stacks: [], shared: [], warnings: [] };
|
|
15544
16057
|
}
|
|
15545
|
-
function isEscape(key) {
|
|
15546
|
-
const name = key.name.toLowerCase();
|
|
15547
|
-
return name === "escape" || name === "esc" || key.sequence === "\x1B";
|
|
15548
|
-
}
|
|
15549
|
-
function isFleetKey(key, value) {
|
|
15550
|
-
return !key.ctrl && !key.meta && !key.option && !key.super && !key.hyper && (key.name === value || key.sequence === value);
|
|
15551
|
-
}
|
|
15552
16058
|
function selectedStack(snapshot, project) {
|
|
15553
16059
|
return snapshot.stacks.find((stack) => stack.project === project);
|
|
15554
16060
|
}
|
|
15555
16061
|
function fleetServiceRowCount(stack) {
|
|
15556
|
-
return stack?.services.reduce((count, service) => count + 1 + (service.
|
|
16062
|
+
return stack?.services.reduce((count, service) => count + 1 + serviceEndpoints(service).length, 0) ?? 0;
|
|
15557
16063
|
}
|
|
15558
16064
|
function selectedFleetEndpoint(stack, serviceName, endpointName) {
|
|
15559
16065
|
const service = stack?.services.find((candidate) => candidate.name === serviceName);
|
|
15560
|
-
const endpoints = service
|
|
16066
|
+
const endpoints = service === undefined ? [] : serviceEndpoints(service);
|
|
15561
16067
|
return endpoints.find((endpoint) => endpoint.name === endpointName) ?? endpoints[0];
|
|
15562
16068
|
}
|
|
15563
16069
|
function usableEndpoint(stack, serviceName, endpointName) {
|
|
15564
16070
|
const endpoint = selectedFleetEndpoint(stack, serviceName, endpointName);
|
|
15565
|
-
|
|
15566
|
-
return;
|
|
15567
|
-
return endpoint.localUrl ?? endpoint.publicUrl ?? endpoint.url ?? `${endpoint.host}:${endpoint.port}`;
|
|
16071
|
+
return endpoint === undefined ? undefined : endpointReach(endpoint);
|
|
15568
16072
|
}
|
|
15569
|
-
function
|
|
15570
|
-
|
|
16073
|
+
function downTargetChanged(confirmed, snapshot) {
|
|
16074
|
+
const current = snapshot.stacks.find((candidate) => candidate.project === confirmed.project);
|
|
16075
|
+
return current?.repoId !== confirmed.repoId || current.worktree !== confirmed.worktree || current.createdAt !== confirmed.createdAt;
|
|
15571
16076
|
}
|
|
15572
16077
|
function fleetLogSelectionKey(stack, serviceName) {
|
|
15573
16078
|
if (stack === undefined || serviceName === undefined)
|
|
@@ -15593,6 +16098,20 @@ function doctorOmissionSummary(rows, visibleRows = 10) {
|
|
|
15593
16098
|
const warnings = omitted.filter((row2) => row2.level === "warn").length;
|
|
15594
16099
|
return `\u2026 ${omitted.length} more (${errors2} errors, ${warnings} warnings)`;
|
|
15595
16100
|
}
|
|
16101
|
+
function budgetDoctorReport(rows, width, lineBudget) {
|
|
16102
|
+
const entries = [];
|
|
16103
|
+
let used = 0;
|
|
16104
|
+
for (const row2 of rows) {
|
|
16105
|
+
const detail = sanitizeFleetTerminalText(row2.detail);
|
|
16106
|
+
const detailLines = detail === "" ? [] : wrapFleetText(detail, Math.max(8, width - 6), 2);
|
|
16107
|
+
const cost = 1 + detailLines.length;
|
|
16108
|
+
if (used + cost > lineBudget)
|
|
16109
|
+
break;
|
|
16110
|
+
entries.push({ row: row2, detailLines });
|
|
16111
|
+
used += cost;
|
|
16112
|
+
}
|
|
16113
|
+
return { entries, shown: entries.length };
|
|
16114
|
+
}
|
|
15596
16115
|
function FleetApp({
|
|
15597
16116
|
source,
|
|
15598
16117
|
preferredProject,
|
|
@@ -15605,13 +16124,29 @@ function FleetApp({
|
|
|
15605
16124
|
const [state, dispatch] = useReducer(reduceFleetUiState, undefined, createFleetUiState);
|
|
15606
16125
|
const [logs, setLogs] = useState([]);
|
|
15607
16126
|
const [connected, setConnected] = useState(false);
|
|
15608
|
-
const [
|
|
16127
|
+
const [connectionNotice, setConnectionNotice] = useState("connecting to hestiad\u2026");
|
|
16128
|
+
const [toast, setToast] = useState(undefined);
|
|
15609
16129
|
const [doctorRows, setDoctorRows] = useState([]);
|
|
15610
16130
|
const [doctorRunning, setDoctorRunning] = useState(false);
|
|
15611
16131
|
const [sharedBusy, setSharedBusy] = useState(false);
|
|
16132
|
+
const [spinnerFrame, setSpinnerFrame] = useState(0);
|
|
16133
|
+
const [now, setNow] = useState(() => Date.now());
|
|
15612
16134
|
const lastFrameAt = useRef(Date.now());
|
|
16135
|
+
const toastTimer = useRef(undefined);
|
|
15613
16136
|
const stateRef = useRef(state);
|
|
15614
16137
|
stateRef.current = state;
|
|
16138
|
+
const showToast = (text2, ttl = TOAST_TTL_MS) => {
|
|
16139
|
+
if (toastTimer.current !== undefined)
|
|
16140
|
+
clearTimeout(toastTimer.current);
|
|
16141
|
+
setToast(text2);
|
|
16142
|
+
toastTimer.current = setTimeout(() => {
|
|
16143
|
+
setToast((current) => current === text2 ? undefined : current);
|
|
16144
|
+
}, ttl);
|
|
16145
|
+
};
|
|
16146
|
+
useEffect(() => () => {
|
|
16147
|
+
if (toastTimer.current !== undefined)
|
|
16148
|
+
clearTimeout(toastTimer.current);
|
|
16149
|
+
}, []);
|
|
15615
16150
|
useEffect(() => {
|
|
15616
16151
|
const controller = new AbortController;
|
|
15617
16152
|
(async () => {
|
|
@@ -15619,23 +16154,24 @@ function FleetApp({
|
|
|
15619
16154
|
lastFrameAt.current = Date.now();
|
|
15620
16155
|
if (frame.type === "snapshot") {
|
|
15621
16156
|
setConnected(true);
|
|
15622
|
-
|
|
16157
|
+
setConnectionNotice(undefined);
|
|
15623
16158
|
setSnapshot(frame.snapshot);
|
|
15624
16159
|
dispatch({ type: "reconcile", snapshot: frame.snapshot, preferredProject });
|
|
15625
16160
|
} else if (frame.sequence === -1) {
|
|
15626
16161
|
setConnected(false);
|
|
15627
|
-
|
|
16162
|
+
setConnectionNotice(frame.at);
|
|
15628
16163
|
} else {
|
|
15629
16164
|
setConnected(true);
|
|
16165
|
+
setConnectionNotice(undefined);
|
|
15630
16166
|
}
|
|
15631
16167
|
}
|
|
15632
16168
|
})();
|
|
15633
16169
|
const staleTimer = setInterval(() => {
|
|
15634
16170
|
if (Date.now() - lastFrameAt.current > 20000) {
|
|
15635
16171
|
setConnected(false);
|
|
15636
|
-
|
|
16172
|
+
setConnectionNotice("hestiad heartbeat is stale; reconnecting\u2026");
|
|
15637
16173
|
}
|
|
15638
|
-
},
|
|
16174
|
+
}, 5000);
|
|
15639
16175
|
return () => {
|
|
15640
16176
|
clearInterval(staleTimer);
|
|
15641
16177
|
controller.abort();
|
|
@@ -15643,8 +16179,12 @@ function FleetApp({
|
|
|
15643
16179
|
}, [preferredProject, source]);
|
|
15644
16180
|
const selectedLogStack = selectedStack(snapshot, state.selection.project);
|
|
15645
16181
|
const selectionKey = fleetLogSelectionKey(selectedLogStack, state.selection.service);
|
|
16182
|
+
const appendedLines = useRef(0);
|
|
16183
|
+
const seenAppendedLines = useRef(0);
|
|
15646
16184
|
useEffect(() => {
|
|
15647
16185
|
setLogs([]);
|
|
16186
|
+
appendedLines.current = 0;
|
|
16187
|
+
seenAppendedLines.current = 0;
|
|
15648
16188
|
if (selectionKey === undefined)
|
|
15649
16189
|
return;
|
|
15650
16190
|
const [project, service] = selectionKey.split("\x00");
|
|
@@ -15657,57 +16197,90 @@ function FleetApp({
|
|
|
15657
16197
|
service: sanitizeFleetTerminalText(line.service),
|
|
15658
16198
|
text: sanitizeFleetTerminalText(line.text)
|
|
15659
16199
|
};
|
|
16200
|
+
appendedLines.current += 1;
|
|
15660
16201
|
setLogs((current) => [...current, sanitized].slice(-LOG_RING_CAPACITY));
|
|
15661
|
-
dispatch({ type: "new-lines", count: 1, maxOffset: LOG_RING_CAPACITY - 1 });
|
|
15662
16202
|
}
|
|
15663
16203
|
})();
|
|
15664
16204
|
return () => controller.abort();
|
|
15665
16205
|
}, [selectionKey, source]);
|
|
15666
16206
|
const stacks = useMemo(() => visibleFleetStacks(snapshot, state.filter), [snapshot, state.filter]);
|
|
15667
16207
|
const stack = selectedStack(snapshot, state.selection.project);
|
|
15668
|
-
const context = {
|
|
16208
|
+
const context = useMemo(() => ({
|
|
15669
16209
|
repo: sanitizeFleetTerminalText(stack?.repo ?? invokingRepository.repo),
|
|
15670
16210
|
branch: sanitizeFleetTerminalText(stack?.branch ?? invokingRepository.branch),
|
|
15671
16211
|
worktree: sanitizeFleetTerminalText(stack?.worktree ?? invokingRepository.worktree),
|
|
15672
16212
|
project: sanitizeFleetTerminalText(stack?.project ?? "\u2014")
|
|
15673
|
-
};
|
|
15674
|
-
const endpointView =
|
|
16213
|
+
}), [stack, invokingRepository]);
|
|
16214
|
+
const endpointView = selectedFleetEndpoint(stack, state.selection.service, state.selection.endpoint);
|
|
15675
16215
|
const endpoint = usableEndpoint(stack, state.selection.service, state.selection.endpoint);
|
|
15676
|
-
const effectiveLayout = state.layout
|
|
15677
|
-
const
|
|
16216
|
+
const effectiveLayout = resolveFleetLayout(state.layout, terminal.width);
|
|
16217
|
+
const paneWidths = fleetPaneWidths(effectiveLayout, terminal.width);
|
|
16218
|
+
const headerRows = 1;
|
|
16219
|
+
const contextRows = 2;
|
|
16220
|
+
const footerRows = 1;
|
|
16221
|
+
const warningRows = stack?.warning === undefined ? 0 : 1;
|
|
16222
|
+
const svcRows = fleetServiceRowCount(stack) + warningRows;
|
|
16223
|
+
const stackedSidebarHeight = stackSidebarHeight(stacks.length);
|
|
16224
|
+
const svcPaneH = servicePaneHeight(svcRows, effectiveLayout === "split" ? 14 : 12);
|
|
16225
|
+
const logHeight = Math.max(5, terminal.height - headerRows - contextRows - footerRows - svcPaneH - (effectiveLayout === "stack" ? stackedSidebarHeight : 0));
|
|
16226
|
+
const logWidth = effectiveLayout === "split" ? paneWidths.main : terminal.width;
|
|
16227
|
+
const logViewportRows = Math.max(1, logHeight - 3);
|
|
16228
|
+
const logRows = useMemo(() => buildFleetLogRows(logs, Math.max(1, logWidth - 4)), [logs, logWidth]);
|
|
16229
|
+
const maxLogOffset = Math.max(0, logRows.length - logViewportRows);
|
|
16230
|
+
useEffect(() => {
|
|
16231
|
+
const appended = appendedLines.current - seenAppendedLines.current;
|
|
16232
|
+
seenAppendedLines.current = appendedLines.current;
|
|
16233
|
+
if (appended > 0 && !stateRef.current.follow) {
|
|
16234
|
+
const fresh = buildFleetLogRows(logs.slice(-Math.min(appended, logs.length)), Math.max(1, logWidth - 4)).length;
|
|
16235
|
+
dispatch({ type: "new-lines", count: fresh, maxOffset: maxLogOffset });
|
|
16236
|
+
}
|
|
16237
|
+
}, [logs, logWidth, maxLogOffset]);
|
|
16238
|
+
const anyStarting = snapshot.stacks.some((candidate) => candidate.phase === "starting");
|
|
16239
|
+
useEffect(() => {
|
|
16240
|
+
if (!anyStarting)
|
|
16241
|
+
return;
|
|
16242
|
+
const timer = setInterval(() => setSpinnerFrame((frame) => (frame + 1) % SPINNER_FRAMES.length), 120);
|
|
16243
|
+
return () => clearInterval(timer);
|
|
16244
|
+
}, [anyStarting]);
|
|
16245
|
+
useEffect(() => {
|
|
16246
|
+
const timer = setInterval(() => setNow(Date.now()), 30000);
|
|
16247
|
+
return () => clearInterval(timer);
|
|
16248
|
+
}, []);
|
|
15678
16249
|
useEffect(() => {
|
|
15679
16250
|
const confirmed = state.confirmDown;
|
|
15680
|
-
|
|
15681
|
-
if (confirmed !== undefined && !state.downPending && (currentIncarnation?.repoId !== confirmed.repoId || currentIncarnation.worktree !== confirmed.worktree || currentIncarnation.createdAt !== confirmed.createdAt)) {
|
|
16251
|
+
if (confirmed !== undefined && !state.downPending && downTargetChanged(confirmed, snapshot)) {
|
|
15682
16252
|
dispatch({ type: "confirm-down" });
|
|
15683
|
-
|
|
16253
|
+
showToast(`${confirmed.project} changed; down cancelled`);
|
|
15684
16254
|
}
|
|
15685
16255
|
}, [snapshot, state.confirmDown, state.downPending]);
|
|
16256
|
+
const copyToClipboard = (value, label) => {
|
|
16257
|
+
const copied = renderer.copyToClipboardOSC52(value);
|
|
16258
|
+
showToast(copied ? `copied ${label ?? value}` : `copy unavailable: ${label ?? value}`);
|
|
16259
|
+
};
|
|
15686
16260
|
useKeyboard((key) => {
|
|
15687
16261
|
const current = stateRef.current;
|
|
15688
16262
|
if (current.confirmDown !== undefined) {
|
|
15689
16263
|
key.preventDefault();
|
|
15690
16264
|
key.stopPropagation();
|
|
15691
|
-
if (
|
|
16265
|
+
if (isEscapeKey(key) && !current.downPending) {
|
|
15692
16266
|
dispatch({ type: "confirm-down" });
|
|
15693
|
-
} else if (!current.downPending && (
|
|
16267
|
+
} else if (!current.downPending && (isPlainKey(key, "d") || isEnterKey(key))) {
|
|
15694
16268
|
const confirmed = current.confirmDown;
|
|
15695
|
-
|
|
15696
|
-
if (currentIncarnation?.repoId !== confirmed.repoId || currentIncarnation.worktree !== confirmed.worktree || currentIncarnation.createdAt !== confirmed.createdAt) {
|
|
16269
|
+
if (downTargetChanged(confirmed, snapshot)) {
|
|
15697
16270
|
dispatch({ type: "confirm-down" });
|
|
15698
|
-
|
|
16271
|
+
showToast(`${confirmed.project} changed; down cancelled`);
|
|
15699
16272
|
return;
|
|
15700
16273
|
}
|
|
15701
16274
|
const project = confirmed.project;
|
|
15702
16275
|
dispatch({ type: "down-pending", pending: true });
|
|
15703
|
-
|
|
16276
|
+
showToast(`tearing down ${project}\u2026`);
|
|
15704
16277
|
source.down(confirmed).then(() => {
|
|
15705
16278
|
dispatch({ type: "down-pending", pending: false });
|
|
15706
16279
|
dispatch({ type: "confirm-down" });
|
|
15707
|
-
|
|
16280
|
+
showToast(`${project} is down; volumes and project images removed`);
|
|
15708
16281
|
}).catch((error) => {
|
|
15709
16282
|
dispatch({ type: "down-pending", pending: false });
|
|
15710
|
-
|
|
16283
|
+
showToast(`down failed: ${error.message}`);
|
|
15711
16284
|
});
|
|
15712
16285
|
}
|
|
15713
16286
|
return;
|
|
@@ -15715,7 +16288,7 @@ function FleetApp({
|
|
|
15715
16288
|
if (current.helpOpen || current.doctorOpen) {
|
|
15716
16289
|
key.preventDefault();
|
|
15717
16290
|
key.stopPropagation();
|
|
15718
|
-
if (
|
|
16291
|
+
if (isEscapeKey(key) || isPlainKey(key, "?")) {
|
|
15719
16292
|
dispatch({ type: current.helpOpen ? "help" : "doctor", open: false });
|
|
15720
16293
|
}
|
|
15721
16294
|
return;
|
|
@@ -15723,45 +16296,45 @@ function FleetApp({
|
|
|
15723
16296
|
if (current.sharedOpen) {
|
|
15724
16297
|
key.preventDefault();
|
|
15725
16298
|
key.stopPropagation();
|
|
15726
|
-
if (
|
|
16299
|
+
if (isEscapeKey(key) || isPlainKey(key, "s"))
|
|
15727
16300
|
return dispatch({ type: "shared", open: false });
|
|
15728
16301
|
const list = snapshot.shared;
|
|
15729
16302
|
if (list.length === 0)
|
|
15730
16303
|
return;
|
|
15731
|
-
if (
|
|
16304
|
+
if (isDownKey(key))
|
|
15732
16305
|
return dispatch({ type: "move-shared", delta: 1, count: list.length });
|
|
15733
|
-
if (
|
|
16306
|
+
if (isUpKey(key))
|
|
15734
16307
|
return dispatch({ type: "move-shared", delta: -1, count: list.length });
|
|
15735
16308
|
const selected = list[Math.min(current.sharedSelection, list.length - 1)];
|
|
15736
16309
|
if (selected === undefined || sharedBusy)
|
|
15737
16310
|
return;
|
|
15738
16311
|
const runShared = (label, action) => {
|
|
15739
16312
|
setSharedBusy(true);
|
|
15740
|
-
|
|
15741
|
-
action.then(() =>
|
|
16313
|
+
showToast(`${label} ${selected.name}\u2026`);
|
|
16314
|
+
action.then(() => showToast(`${label} ${selected.name}: done`)).catch((error) => showToast(`${label} ${selected.name} failed: ${error.message}`)).finally(() => setSharedBusy(false));
|
|
15742
16315
|
};
|
|
15743
|
-
if (
|
|
16316
|
+
if (isPlainKey(key, "a") || isPlainKey(key, "x") || isPlainKey(key, "r")) {
|
|
15744
16317
|
if (selected.holder?.mine !== true) {
|
|
15745
|
-
|
|
16318
|
+
showToast(`this worktree does not hold ${selected.name}`);
|
|
15746
16319
|
return;
|
|
15747
16320
|
}
|
|
15748
16321
|
const worktree = selected.holder.worktree;
|
|
15749
|
-
if (
|
|
16322
|
+
if (isPlainKey(key, "a"))
|
|
15750
16323
|
runShared("allowing", source.allowShared(worktree, selected.name));
|
|
15751
|
-
else if (
|
|
16324
|
+
else if (isPlainKey(key, "x"))
|
|
15752
16325
|
runShared("denying", source.denyShared(worktree, selected.name));
|
|
15753
16326
|
else
|
|
15754
16327
|
runShared("releasing", source.releaseShared(worktree, selected.name));
|
|
15755
16328
|
return;
|
|
15756
16329
|
}
|
|
15757
|
-
if (
|
|
16330
|
+
if (isPlainKey(key, "c")) {
|
|
15758
16331
|
const acting = selectedStack(snapshot, current.selection.project);
|
|
15759
16332
|
if (acting === undefined) {
|
|
15760
|
-
|
|
16333
|
+
showToast("select a stack (in the fleet list) to claim as");
|
|
15761
16334
|
return;
|
|
15762
16335
|
}
|
|
15763
16336
|
if (selected.holder?.project === acting.project) {
|
|
15764
|
-
|
|
16337
|
+
showToast(`${acting.project} already holds ${selected.name}`);
|
|
15765
16338
|
return;
|
|
15766
16339
|
}
|
|
15767
16340
|
runShared(`claiming as ${acting.project},`, source.claimShared(acting.worktree, selected.name));
|
|
@@ -15770,41 +16343,59 @@ function FleetApp({
|
|
|
15770
16343
|
return;
|
|
15771
16344
|
}
|
|
15772
16345
|
if (current.focus === "filter") {
|
|
15773
|
-
if (
|
|
16346
|
+
if (isEscapeKey(key)) {
|
|
16347
|
+
key.preventDefault();
|
|
16348
|
+
key.stopPropagation();
|
|
16349
|
+
if (current.filter !== "")
|
|
16350
|
+
dispatch({ type: "filter", filter: "", snapshot });
|
|
16351
|
+
else
|
|
16352
|
+
dispatch({ type: "focus", focus: "stacks" });
|
|
16353
|
+
return;
|
|
16354
|
+
}
|
|
16355
|
+
if (isEnterKey(key)) {
|
|
15774
16356
|
key.preventDefault();
|
|
15775
16357
|
key.stopPropagation();
|
|
15776
16358
|
dispatch({ type: "focus", focus: "stacks" });
|
|
15777
16359
|
}
|
|
15778
16360
|
return;
|
|
15779
16361
|
}
|
|
15780
|
-
if (
|
|
16362
|
+
if (isPlainKey(key, "q"))
|
|
15781
16363
|
return onQuit();
|
|
15782
|
-
if (
|
|
16364
|
+
if (isPlainKey(key, "?"))
|
|
15783
16365
|
return dispatch({ type: "help", open: true });
|
|
15784
|
-
if (
|
|
16366
|
+
if (isPlainKey(key, "s"))
|
|
15785
16367
|
return dispatch({ type: "shared", open: true });
|
|
15786
|
-
if (
|
|
16368
|
+
if (isPlainKey(key, "/"))
|
|
15787
16369
|
return dispatch({ type: "focus", focus: "filter" });
|
|
15788
|
-
if (
|
|
16370
|
+
if (isPlainKey(key, "0"))
|
|
15789
16371
|
return dispatch({ type: "layout", layout: "auto" });
|
|
15790
|
-
if (
|
|
16372
|
+
if (isPlainKey(key, "1"))
|
|
15791
16373
|
return dispatch({ type: "layout", layout: "split" });
|
|
15792
|
-
if (
|
|
16374
|
+
if (isPlainKey(key, "2"))
|
|
15793
16375
|
return dispatch({ type: "layout", layout: "stack" });
|
|
15794
16376
|
if (key.name === "tab") {
|
|
16377
|
+
const focusOrder = ["stacks", "services", "logs"];
|
|
15795
16378
|
const index = focusOrder.indexOf(current.focus);
|
|
15796
16379
|
const delta2 = key.shift ? -1 : 1;
|
|
15797
16380
|
const next = (index + delta2 + focusOrder.length) % focusOrder.length;
|
|
15798
16381
|
return dispatch({ type: "focus", focus: focusOrder[next] });
|
|
15799
16382
|
}
|
|
15800
|
-
if (
|
|
16383
|
+
if (isPlainKey(key, ","))
|
|
16384
|
+
return dispatch({ type: "move-stack", delta: -1, snapshot });
|
|
16385
|
+
if (isPlainKey(key, "."))
|
|
16386
|
+
return dispatch({ type: "move-stack", delta: 1, snapshot });
|
|
16387
|
+
if (isPlainKey(key, "["))
|
|
15801
16388
|
return dispatch({ type: "move-service", delta: -1, snapshot });
|
|
15802
|
-
if (
|
|
16389
|
+
if (isPlainKey(key, "]"))
|
|
15803
16390
|
return dispatch({ type: "move-service", delta: 1, snapshot });
|
|
15804
|
-
if (
|
|
16391
|
+
if (isPlainKey(key, "f"))
|
|
16392
|
+
return dispatch({ type: "follow", follow: !current.follow });
|
|
16393
|
+
if (isFollowBottomKey(key))
|
|
15805
16394
|
return dispatch({ type: "follow", follow: true });
|
|
16395
|
+
if (isScrollTopKey(key)) {
|
|
16396
|
+
return dispatch({ type: "scroll-logs", delta: -logRows.length, maxOffset: maxLogOffset });
|
|
15806
16397
|
}
|
|
15807
|
-
if (
|
|
16398
|
+
if (isDoctorKey(key)) {
|
|
15808
16399
|
if (doctorRunning)
|
|
15809
16400
|
return;
|
|
15810
16401
|
dispatch({ type: "doctor", open: true });
|
|
@@ -15812,40 +16403,52 @@ function FleetApp({
|
|
|
15812
16403
|
setDoctorRows([]);
|
|
15813
16404
|
const worktree = stack !== undefined && existsSync23(stack.worktree) ? stack.worktree : process.cwd();
|
|
15814
16405
|
source.diagnose(worktree).then(setDoctorRows).catch((error) => {
|
|
15815
|
-
|
|
16406
|
+
showToast(`doctor failed: ${error.message}`);
|
|
15816
16407
|
}).finally(() => setDoctorRunning(false));
|
|
15817
16408
|
return;
|
|
15818
16409
|
}
|
|
15819
|
-
if (
|
|
16410
|
+
if (isPlainKey(key, "d")) {
|
|
15820
16411
|
if (stack?.phase === "queued" || stack?.phase === "reserved") {
|
|
15821
|
-
|
|
16412
|
+
showToast(`${stack.project} is ${stack.phase}; down is available after startup begins`);
|
|
15822
16413
|
} else if (stack !== undefined) {
|
|
15823
16414
|
dispatch({ type: "confirm-down", stack });
|
|
15824
16415
|
}
|
|
15825
16416
|
return;
|
|
15826
16417
|
}
|
|
15827
|
-
if (
|
|
16418
|
+
if (isPlainKey(key, "o") && endpoint !== undefined) {
|
|
15828
16419
|
openFleetUrl(endpoint);
|
|
15829
|
-
|
|
16420
|
+
showToast(endpoint);
|
|
15830
16421
|
return;
|
|
15831
16422
|
}
|
|
15832
|
-
if (
|
|
15833
|
-
|
|
15834
|
-
|
|
16423
|
+
if (isShiftedKey(key, "y")) {
|
|
16424
|
+
if (stack === undefined)
|
|
16425
|
+
return;
|
|
16426
|
+
const env = buildEnvBlock(stack);
|
|
16427
|
+
if (env === "")
|
|
16428
|
+
showToast(`no env surface recorded for ${stack.project}`);
|
|
16429
|
+
else
|
|
16430
|
+
copyToClipboard(env, `env block (${env.split(`
|
|
16431
|
+
`).length} keys)`);
|
|
15835
16432
|
return;
|
|
15836
16433
|
}
|
|
15837
|
-
|
|
15838
|
-
|
|
15839
|
-
if (surface !== undefined) {
|
|
15840
|
-
const copied = renderer.copyToClipboardOSC52(surface);
|
|
15841
|
-
setNotice(copied ? `copied ${surface}` : `copy unavailable: ${surface}`);
|
|
16434
|
+
if (isPlainKey(key, "y") && endpoint !== undefined) {
|
|
16435
|
+
copyToClipboard(endpoint);
|
|
15842
16436
|
return;
|
|
15843
16437
|
}
|
|
15844
|
-
|
|
15845
|
-
|
|
16438
|
+
const copySurfaces = [
|
|
16439
|
+
["c", endpointView?.url],
|
|
16440
|
+
["l", endpointView?.localUrl],
|
|
16441
|
+
["p", endpointView?.publicUrl]
|
|
16442
|
+
];
|
|
16443
|
+
const copySurface = copySurfaces.find(([name]) => isPlainKey(key, name));
|
|
16444
|
+
if (copySurface !== undefined) {
|
|
16445
|
+
if (copySurface[1] !== undefined)
|
|
16446
|
+
copyToClipboard(copySurface[1]);
|
|
16447
|
+
else
|
|
16448
|
+
showToast("selected URL surface is unavailable");
|
|
15846
16449
|
return;
|
|
15847
16450
|
}
|
|
15848
|
-
const delta =
|
|
16451
|
+
const delta = isDownKey(key) ? 1 : isUpKey(key) ? -1 : 0;
|
|
15849
16452
|
if (delta === 0)
|
|
15850
16453
|
return;
|
|
15851
16454
|
if (current.focus === "stacks")
|
|
@@ -15853,11 +16456,46 @@ function FleetApp({
|
|
|
15853
16456
|
else if (current.focus === "services")
|
|
15854
16457
|
dispatch({ type: "move-service", delta, snapshot });
|
|
15855
16458
|
else
|
|
15856
|
-
dispatch({ type: "scroll-logs", delta });
|
|
16459
|
+
dispatch({ type: "scroll-logs", delta, maxOffset: maxLogOffset });
|
|
15857
16460
|
});
|
|
15858
|
-
const
|
|
15859
|
-
const status = connected ? `${capacity.live}/${capacity.maxStacks} live \xB7 ${capacity.reserved} reserved \xB7 ${capacity.queued} queued` : "disconnected";
|
|
16461
|
+
const uptime = stack?.createdAt !== undefined && (stack.phase === "up" || stack.phase === "degraded" || stack.phase === "starting") ? formatUptime(stack.createdAt, now) : undefined;
|
|
15860
16462
|
const logLabel = state.selection.service ?? "select a service";
|
|
16463
|
+
const statusNotice = resolveStatusNotice(connectionNotice, toast);
|
|
16464
|
+
const doctorWidth = Math.min(72, Math.max(30, terminal.width - 6));
|
|
16465
|
+
const wideFooter = terminal.width >= 100;
|
|
16466
|
+
const onSelectProject = useCallback((project) => dispatch({ type: "select-stack", project, snapshot }), [snapshot]);
|
|
16467
|
+
const onSelectService = useCallback((service) => dispatch({ type: "select-service", service, snapshot }), [snapshot]);
|
|
16468
|
+
const onSelectEndpoint = useCallback((service, endpointName) => dispatch({ type: "select-endpoint", service, endpoint: endpointName, snapshot }), [snapshot]);
|
|
16469
|
+
const servicePane = /* @__PURE__ */ jsxDEV5(ServicePane, {
|
|
16470
|
+
stack,
|
|
16471
|
+
uptime,
|
|
16472
|
+
selectedService: state.selection.service,
|
|
16473
|
+
selectedEndpoint: state.selection.endpoint,
|
|
16474
|
+
width: logWidth,
|
|
16475
|
+
focused: state.focus === "services",
|
|
16476
|
+
onSelectService,
|
|
16477
|
+
onSelectEndpoint
|
|
16478
|
+
}, undefined, false, undefined, this);
|
|
16479
|
+
const sidebar = (width) => /* @__PURE__ */ jsxDEV5(StackSidebar, {
|
|
16480
|
+
stacks,
|
|
16481
|
+
capacity: snapshot.capacity,
|
|
16482
|
+
selectedProject: state.selection.project,
|
|
16483
|
+
width,
|
|
16484
|
+
focused: state.focus === "stacks",
|
|
16485
|
+
spinnerFrame,
|
|
16486
|
+
onSelectProject
|
|
16487
|
+
}, undefined, false, undefined, this);
|
|
16488
|
+
const logPane = /* @__PURE__ */ jsxDEV5(LogPane, {
|
|
16489
|
+
rows: logRows,
|
|
16490
|
+
height: logHeight,
|
|
16491
|
+
width: logWidth,
|
|
16492
|
+
offset: state.logOffset,
|
|
16493
|
+
follow: state.follow,
|
|
16494
|
+
unseen: state.unseenLines,
|
|
16495
|
+
label: logLabel,
|
|
16496
|
+
focused: state.focus === "logs",
|
|
16497
|
+
onScroll: (delta) => dispatch({ type: "scroll-logs", delta, maxOffset: maxLogOffset })
|
|
16498
|
+
}, undefined, false, undefined, this);
|
|
15861
16499
|
return /* @__PURE__ */ jsxDEV5("box", {
|
|
15862
16500
|
style: { width: "100%", height: "100%", flexDirection: "column", backgroundColor: fleetTheme.background },
|
|
15863
16501
|
children: [
|
|
@@ -15866,70 +16504,64 @@ function FleetApp({
|
|
|
15866
16504
|
children: [
|
|
15867
16505
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
15868
16506
|
fg: fleetTheme.accent,
|
|
15869
|
-
children:
|
|
15870
|
-
|
|
15871
|
-
context.repo
|
|
15872
|
-
]
|
|
15873
|
-
}, undefined, true, undefined, this),
|
|
15874
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
15875
|
-
fg: fleetTheme.muted,
|
|
15876
|
-
children: [
|
|
15877
|
-
" ",
|
|
15878
|
-
status
|
|
15879
|
-
]
|
|
15880
|
-
}, undefined, true, undefined, this)
|
|
15881
|
-
]
|
|
15882
|
-
}, undefined, true, undefined, this),
|
|
15883
|
-
terminal.width >= 90 ? /* @__PURE__ */ jsxDEV5("box", {
|
|
15884
|
-
style: { height: 4, paddingLeft: 1, flexDirection: "column", backgroundColor: fleetTheme.background },
|
|
15885
|
-
children: [
|
|
15886
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
15887
|
-
fg: fleetTheme.text,
|
|
15888
|
-
children: [
|
|
15889
|
-
"Repository ",
|
|
15890
|
-
context.repo
|
|
15891
|
-
]
|
|
15892
|
-
}, undefined, true, undefined, this),
|
|
16507
|
+
children: "Hestia Fleet"
|
|
16508
|
+
}, undefined, false, undefined, this),
|
|
15893
16509
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
15894
|
-
fg: fleetTheme.
|
|
15895
|
-
children:
|
|
15896
|
-
|
|
15897
|
-
context.branch
|
|
15898
|
-
]
|
|
15899
|
-
}, undefined, true, undefined, this),
|
|
16510
|
+
fg: fleetTheme.faint,
|
|
16511
|
+
children: " \xB7 "
|
|
16512
|
+
}, undefined, false, undefined, this),
|
|
15900
16513
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
15901
16514
|
fg: fleetTheme.text,
|
|
15902
|
-
children:
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
}, undefined,
|
|
16515
|
+
children: fitFleetText(context.repo, Math.max(8, terminal.width - 30))
|
|
16516
|
+
}, undefined, false, undefined, this),
|
|
16517
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16518
|
+
style: { flexGrow: 1 }
|
|
16519
|
+
}, undefined, false, undefined, this),
|
|
15907
16520
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
15908
|
-
fg: fleetTheme.
|
|
15909
|
-
children:
|
|
15910
|
-
|
|
15911
|
-
context.project
|
|
15912
|
-
]
|
|
15913
|
-
}, undefined, true, undefined, this)
|
|
16521
|
+
fg: connected ? fleetTheme.healthy : fleetTheme.warning,
|
|
16522
|
+
children: connected ? "\u25CF connected" : "\u25CB reconnecting"
|
|
16523
|
+
}, undefined, false, undefined, this)
|
|
15914
16524
|
]
|
|
15915
|
-
}, undefined, true, undefined, this)
|
|
16525
|
+
}, undefined, true, undefined, this),
|
|
16526
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
15916
16527
|
style: { height: 2, paddingLeft: 1, flexDirection: "column", backgroundColor: fleetTheme.background },
|
|
15917
16528
|
children: [
|
|
15918
|
-
/* @__PURE__ */ jsxDEV5("
|
|
15919
|
-
|
|
16529
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16530
|
+
style: { height: 1, flexDirection: "row" },
|
|
15920
16531
|
children: [
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
16532
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16533
|
+
fg: fleetTheme.muted,
|
|
16534
|
+
children: fitFleetText(context.repo, 24)
|
|
16535
|
+
}, undefined, false, undefined, this),
|
|
16536
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16537
|
+
fg: fleetTheme.faint,
|
|
16538
|
+
children: " / "
|
|
16539
|
+
}, undefined, false, undefined, this),
|
|
16540
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16541
|
+
fg: fleetTheme.text,
|
|
16542
|
+
children: context.branch
|
|
16543
|
+
}, undefined, false, undefined, this),
|
|
16544
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16545
|
+
fg: fleetTheme.faint,
|
|
16546
|
+
children: " \xB7 "
|
|
16547
|
+
}, undefined, false, undefined, this),
|
|
16548
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16549
|
+
fg: fleetTheme.muted,
|
|
16550
|
+
children: context.project
|
|
16551
|
+
}, undefined, false, undefined, this)
|
|
15926
16552
|
]
|
|
15927
16553
|
}, undefined, true, undefined, this),
|
|
15928
|
-
/* @__PURE__ */ jsxDEV5("
|
|
15929
|
-
|
|
16554
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16555
|
+
style: { height: 1, flexDirection: "row" },
|
|
15930
16556
|
children: [
|
|
15931
|
-
|
|
15932
|
-
|
|
16557
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16558
|
+
fg: fleetTheme.faint,
|
|
16559
|
+
children: "worktree "
|
|
16560
|
+
}, undefined, false, undefined, this),
|
|
16561
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16562
|
+
fg: fleetTheme.muted,
|
|
16563
|
+
children: middleTruncateWorktreePath(context.worktree, Math.max(16, terminal.width - 11))
|
|
16564
|
+
}, undefined, false, undefined, this)
|
|
15933
16565
|
]
|
|
15934
16566
|
}, undefined, true, undefined, this)
|
|
15935
16567
|
]
|
|
@@ -15937,37 +16569,17 @@ function FleetApp({
|
|
|
15937
16569
|
effectiveLayout === "split" ? /* @__PURE__ */ jsxDEV5("box", {
|
|
15938
16570
|
style: { flexGrow: 1, flexDirection: "row" },
|
|
15939
16571
|
children: [
|
|
15940
|
-
|
|
15941
|
-
stacks,
|
|
15942
|
-
selectedProject: state.selection.project,
|
|
15943
|
-
width: Math.max(28, Math.floor(terminal.width * 0.28)),
|
|
15944
|
-
onSelectProject: (project) => dispatch({ type: "select-stack", project, snapshot })
|
|
15945
|
-
}, undefined, false, undefined, this),
|
|
16572
|
+
sidebar(paneWidths.sidebar),
|
|
15946
16573
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15947
16574
|
style: { flexGrow: 1, flexDirection: "column" },
|
|
15948
16575
|
children: [
|
|
15949
16576
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15950
|
-
style: { height:
|
|
15951
|
-
children:
|
|
15952
|
-
stack,
|
|
15953
|
-
selectedService: state.selection.service,
|
|
15954
|
-
selectedEndpoint: state.selection.endpoint,
|
|
15955
|
-
onSelectService: (service) => dispatch({ type: "select-service", service, snapshot }),
|
|
15956
|
-
onSelectEndpoint: (service, endpoint2) => dispatch({ type: "select-endpoint", service, endpoint: endpoint2, snapshot })
|
|
15957
|
-
}, undefined, false, undefined, this)
|
|
16577
|
+
style: { height: svcPaneH },
|
|
16578
|
+
children: servicePane
|
|
15958
16579
|
}, undefined, false, undefined, this),
|
|
15959
16580
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15960
16581
|
style: { flexGrow: 1 },
|
|
15961
|
-
children:
|
|
15962
|
-
lines: logs,
|
|
15963
|
-
height: Math.max(5, terminal.height - 13),
|
|
15964
|
-
width: Math.max(20, Math.floor(terminal.width * 0.72)),
|
|
15965
|
-
offset: state.logOffset,
|
|
15966
|
-
follow: state.follow,
|
|
15967
|
-
unseen: state.unseenLines,
|
|
15968
|
-
label: logLabel,
|
|
15969
|
-
onScroll: (delta) => dispatch({ type: "scroll-logs", delta })
|
|
15970
|
-
}, undefined, false, undefined, this)
|
|
16582
|
+
children: logPane
|
|
15971
16583
|
}, undefined, false, undefined, this)
|
|
15972
16584
|
]
|
|
15973
16585
|
}, undefined, true, undefined, this)
|
|
@@ -15976,42 +16588,22 @@ function FleetApp({
|
|
|
15976
16588
|
style: { flexGrow: 1, flexDirection: "column" },
|
|
15977
16589
|
children: [
|
|
15978
16590
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15979
|
-
style: { height:
|
|
15980
|
-
children:
|
|
15981
|
-
stacks,
|
|
15982
|
-
selectedProject: state.selection.project,
|
|
15983
|
-
width: terminal.width,
|
|
15984
|
-
onSelectProject: (project) => dispatch({ type: "select-stack", project, snapshot })
|
|
15985
|
-
}, undefined, false, undefined, this)
|
|
16591
|
+
style: { height: stackedSidebarHeight },
|
|
16592
|
+
children: sidebar(terminal.width)
|
|
15986
16593
|
}, undefined, false, undefined, this),
|
|
15987
16594
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15988
|
-
style: { height:
|
|
15989
|
-
children:
|
|
15990
|
-
stack,
|
|
15991
|
-
selectedService: state.selection.service,
|
|
15992
|
-
selectedEndpoint: state.selection.endpoint,
|
|
15993
|
-
onSelectService: (service) => dispatch({ type: "select-service", service, snapshot }),
|
|
15994
|
-
onSelectEndpoint: (service, endpoint2) => dispatch({ type: "select-endpoint", service, endpoint: endpoint2, snapshot })
|
|
15995
|
-
}, undefined, false, undefined, this)
|
|
16595
|
+
style: { height: svcPaneH },
|
|
16596
|
+
children: servicePane
|
|
15996
16597
|
}, undefined, false, undefined, this),
|
|
15997
16598
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
15998
16599
|
style: { flexGrow: 1 },
|
|
15999
|
-
children:
|
|
16000
|
-
lines: logs,
|
|
16001
|
-
height: Math.max(5, terminal.height - 17),
|
|
16002
|
-
width: terminal.width,
|
|
16003
|
-
offset: state.logOffset,
|
|
16004
|
-
follow: state.follow,
|
|
16005
|
-
unseen: state.unseenLines,
|
|
16006
|
-
label: logLabel,
|
|
16007
|
-
onScroll: (delta) => dispatch({ type: "scroll-logs", delta })
|
|
16008
|
-
}, undefined, false, undefined, this)
|
|
16600
|
+
children: logPane
|
|
16009
16601
|
}, undefined, false, undefined, this)
|
|
16010
16602
|
]
|
|
16011
16603
|
}, undefined, true, undefined, this),
|
|
16012
16604
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
16013
16605
|
style: { height: 1, paddingLeft: 1, paddingRight: 1, flexDirection: "row", backgroundColor: fleetTheme.panelAlt },
|
|
16014
|
-
children: state.focus === "filter" ? /* @__PURE__ */ jsxDEV5(
|
|
16606
|
+
children: state.focus === "filter" ? /* @__PURE__ */ jsxDEV5(Fragment3, {
|
|
16015
16607
|
children: [
|
|
16016
16608
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
16017
16609
|
fg: fleetTheme.accent,
|
|
@@ -16026,76 +16618,106 @@ function FleetApp({
|
|
|
16026
16618
|
onSubmit: () => dispatch({ type: "focus", focus: "stacks" })
|
|
16027
16619
|
}, undefined, false, undefined, this)
|
|
16028
16620
|
]
|
|
16029
|
-
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV5(
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16621
|
+
}, undefined, true, undefined, this) : statusNotice !== undefined ? /* @__PURE__ */ jsxDEV5(Fragment3, {
|
|
16622
|
+
children: [
|
|
16623
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16624
|
+
fg: connectionNotice !== undefined ? fleetTheme.warning : fleetTheme.text,
|
|
16625
|
+
children: fitFleetText(statusNotice, Math.max(8, terminal.width - 20))
|
|
16626
|
+
}, undefined, false, undefined, this),
|
|
16627
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16628
|
+
style: { flexGrow: 1 }
|
|
16629
|
+
}, undefined, false, undefined, this),
|
|
16630
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16631
|
+
fg: connected ? fleetTheme.muted : fleetTheme.warning,
|
|
16632
|
+
children: fleetCapacitySummary(snapshot.capacity, connected)
|
|
16633
|
+
}, undefined, false, undefined, this)
|
|
16634
|
+
]
|
|
16635
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV5(Fragment3, {
|
|
16636
|
+
children: [
|
|
16637
|
+
wideFooter ? /* @__PURE__ */ jsxDEV5("text", {
|
|
16638
|
+
children: FLEET_KEY_HINTS.map((hint) => /* @__PURE__ */ jsxDEV5("span", {
|
|
16639
|
+
children: [
|
|
16640
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
16641
|
+
fg: fleetTheme.bright,
|
|
16642
|
+
bg: fleetTheme.keycapBg,
|
|
16643
|
+
children: ` ${hint.keys} `
|
|
16644
|
+
}, undefined, false, undefined, this),
|
|
16645
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
16646
|
+
fg: fleetTheme.muted,
|
|
16647
|
+
children: ` ${hint.label} `
|
|
16648
|
+
}, undefined, false, undefined, this)
|
|
16649
|
+
]
|
|
16650
|
+
}, hint.keys, true, undefined, this))
|
|
16651
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV5("text", {
|
|
16652
|
+
fg: fleetTheme.muted,
|
|
16653
|
+
children: "? help \xB7 q quit"
|
|
16654
|
+
}, undefined, false, undefined, this),
|
|
16655
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16656
|
+
style: { flexGrow: 1 }
|
|
16657
|
+
}, undefined, false, undefined, this),
|
|
16658
|
+
state.filter !== "" ? /* @__PURE__ */ jsxDEV5("text", {
|
|
16659
|
+
fg: fleetTheme.accent,
|
|
16660
|
+
children: `filter=${fitFleetText(state.filter, 16)} `
|
|
16661
|
+
}, undefined, false, undefined, this) : null,
|
|
16662
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16663
|
+
fg: connected ? fleetTheme.muted : fleetTheme.warning,
|
|
16664
|
+
children: fleetCapacitySummary(snapshot.capacity, connected)
|
|
16665
|
+
}, undefined, false, undefined, this)
|
|
16666
|
+
]
|
|
16667
|
+
}, undefined, true, undefined, this)
|
|
16033
16668
|
}, undefined, false, undefined, this),
|
|
16034
16669
|
state.helpOpen ? /* @__PURE__ */ jsxDEV5(FleetModal, {
|
|
16035
16670
|
title: "Fleet help",
|
|
16036
16671
|
terminalWidth: terminal.width,
|
|
16037
16672
|
terminalHeight: terminal.height,
|
|
16038
|
-
children: [
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
fg: fleetTheme.text,
|
|
16053
|
-
children: "[ / ] previous / next service"
|
|
16054
|
-
}, undefined, false, undefined, this),
|
|
16055
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
16056
|
-
fg: fleetTheme.text,
|
|
16057
|
-
children: "/ filter \xB7 G follow logs \xB7 0/1/2 layout"
|
|
16058
|
-
}, undefined, false, undefined, this),
|
|
16059
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
16060
|
-
fg: fleetTheme.text,
|
|
16061
|
-
children: "o open \xB7 y primary \xB7 c direct \xB7 l local \xB7 p public"
|
|
16062
|
-
}, undefined, false, undefined, this),
|
|
16063
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
16064
|
-
fg: fleetTheme.text,
|
|
16065
|
-
children: "D doctor \xB7 s shared hostnames (claim / allow / deny / release)"
|
|
16066
|
-
}, undefined, false, undefined, this),
|
|
16067
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
16068
|
-
fg: fleetTheme.danger,
|
|
16069
|
-
children: "d confirmed down (named volumes retained)"
|
|
16070
|
-
}, undefined, false, undefined, this),
|
|
16071
|
-
/* @__PURE__ */ jsxDEV5("text", {
|
|
16072
|
-
fg: fleetTheme.muted,
|
|
16073
|
-
children: "Esc closes this dialog"
|
|
16074
|
-
}, undefined, false, undefined, this)
|
|
16075
|
-
]
|
|
16076
|
-
}, undefined, true, undefined, this) : null,
|
|
16673
|
+
children: HELP_ROWS.map(([keys, action]) => /* @__PURE__ */ jsxDEV5("box", {
|
|
16674
|
+
style: { height: 1, flexDirection: "row" },
|
|
16675
|
+
children: [
|
|
16676
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16677
|
+
fg: fleetTheme.accent,
|
|
16678
|
+
children: padFleetText(keys, 9)
|
|
16679
|
+
}, undefined, false, undefined, this),
|
|
16680
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16681
|
+
fg: fleetTheme.muted,
|
|
16682
|
+
children: action
|
|
16683
|
+
}, undefined, false, undefined, this)
|
|
16684
|
+
]
|
|
16685
|
+
}, keys, true, undefined, this))
|
|
16686
|
+
}, undefined, false, undefined, this) : null,
|
|
16077
16687
|
state.doctorOpen ? /* @__PURE__ */ jsxDEV5(FleetModal, {
|
|
16078
16688
|
title: "hestia doctor",
|
|
16079
16689
|
terminalWidth: terminal.width,
|
|
16080
16690
|
terminalHeight: terminal.height,
|
|
16081
|
-
children:
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
|
|
16098
|
-
|
|
16691
|
+
children: doctorRunning ? /* @__PURE__ */ jsxDEV5("text", {
|
|
16692
|
+
fg: fleetTheme.muted,
|
|
16693
|
+
children: "Running diagnostics\u2026"
|
|
16694
|
+
}, undefined, false, undefined, this) : (() => {
|
|
16695
|
+
const modalHeight = Math.min(22, Math.max(8, terminal.height - 4));
|
|
16696
|
+
const report = budgetDoctorReport(doctorRows, doctorWidth, Math.max(3, modalHeight - 6));
|
|
16697
|
+
const summary = doctorOmissionSummary(doctorRows, report.shown);
|
|
16698
|
+
return [
|
|
16699
|
+
...report.entries.flatMap(({ row: row2, detailLines }) => {
|
|
16700
|
+
const mark = row2.level === "ok" ? "\u2713" : row2.level === "error" ? "\u2717" : row2.level === "warn" ? "!" : "?";
|
|
16701
|
+
const color = row2.level === "error" ? fleetTheme.danger : row2.level === "warn" ? fleetTheme.warning : row2.level === "ok" ? fleetTheme.healthy : fleetTheme.muted;
|
|
16702
|
+
const head = `${mark} ${sanitizeFleetTerminalText(row2.check)}`;
|
|
16703
|
+
return [
|
|
16704
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16705
|
+
fg: color,
|
|
16706
|
+
children: fitFleetText(head, doctorWidth - 4)
|
|
16707
|
+
}, `${row2.check}:head`, false, undefined, this),
|
|
16708
|
+
...detailLines.map((part, index) => /* @__PURE__ */ jsxDEV5("text", {
|
|
16709
|
+
fg: fleetTheme.muted,
|
|
16710
|
+
children: ` ${part}`
|
|
16711
|
+
}, `${row2.check}:detail:${index}`, false, undefined, this))
|
|
16712
|
+
];
|
|
16713
|
+
}),
|
|
16714
|
+
summary === undefined ? null : /* @__PURE__ */ jsxDEV5("text", {
|
|
16715
|
+
fg: fleetTheme.warning,
|
|
16716
|
+
children: summary
|
|
16717
|
+
}, "doctor:omitted", false, undefined, this)
|
|
16718
|
+
];
|
|
16719
|
+
})()
|
|
16720
|
+
}, undefined, false, undefined, this) : null,
|
|
16099
16721
|
state.sharedOpen ? /* @__PURE__ */ jsxDEV5(FleetModal, {
|
|
16100
16722
|
title: "Shared hostnames",
|
|
16101
16723
|
terminalWidth: terminal.width,
|
|
@@ -16112,7 +16734,7 @@ function FleetApp({
|
|
|
16112
16734
|
children: [
|
|
16113
16735
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
16114
16736
|
fg: active ? fleetTheme.accent : fleetTheme.text,
|
|
16115
|
-
children: `${active ? "\
|
|
16737
|
+
children: `${active ? "\u258E" : " "} ${sanitizeFleetTerminalText(entry.name)} ${sanitizeFleetTerminalText(entry.url)} ${holder}`
|
|
16116
16738
|
}, undefined, false, undefined, this),
|
|
16117
16739
|
entry.queue.map((waiter, position) => /* @__PURE__ */ jsxDEV5("text", {
|
|
16118
16740
|
fg: fleetTheme.muted,
|
|
@@ -16135,32 +16757,56 @@ function FleetApp({
|
|
|
16135
16757
|
terminalWidth: terminal.width,
|
|
16136
16758
|
terminalHeight: terminal.height,
|
|
16137
16759
|
children: [
|
|
16138
|
-
/* @__PURE__ */ jsxDEV5("
|
|
16139
|
-
|
|
16760
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16761
|
+
style: { height: 1, flexDirection: "row" },
|
|
16140
16762
|
children: [
|
|
16141
|
-
|
|
16142
|
-
|
|
16763
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16764
|
+
fg: fleetTheme.faint,
|
|
16765
|
+
children: padFleetText("Branch", 12)
|
|
16766
|
+
}, undefined, false, undefined, this),
|
|
16767
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16768
|
+
fg: fleetTheme.bright,
|
|
16769
|
+
children: sanitizeFleetTerminalText(state.confirmDown.branch)
|
|
16770
|
+
}, undefined, false, undefined, this)
|
|
16143
16771
|
]
|
|
16144
16772
|
}, undefined, true, undefined, this),
|
|
16145
|
-
/* @__PURE__ */ jsxDEV5("
|
|
16146
|
-
|
|
16773
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16774
|
+
style: { height: 1, flexDirection: "row" },
|
|
16147
16775
|
children: [
|
|
16148
|
-
|
|
16149
|
-
|
|
16776
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16777
|
+
fg: fleetTheme.faint,
|
|
16778
|
+
children: padFleetText("Repository", 12)
|
|
16779
|
+
}, undefined, false, undefined, this),
|
|
16780
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16781
|
+
fg: fleetTheme.text,
|
|
16782
|
+
children: sanitizeFleetTerminalText(state.confirmDown.repo)
|
|
16783
|
+
}, undefined, false, undefined, this)
|
|
16150
16784
|
]
|
|
16151
16785
|
}, undefined, true, undefined, this),
|
|
16152
|
-
/* @__PURE__ */ jsxDEV5("
|
|
16153
|
-
|
|
16786
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16787
|
+
style: { height: 1, flexDirection: "row" },
|
|
16154
16788
|
children: [
|
|
16155
|
-
|
|
16156
|
-
|
|
16789
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16790
|
+
fg: fleetTheme.faint,
|
|
16791
|
+
children: padFleetText("Worktree", 12)
|
|
16792
|
+
}, undefined, false, undefined, this),
|
|
16793
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16794
|
+
fg: fleetTheme.text,
|
|
16795
|
+
children: sanitizeFleetTerminalText(state.confirmDown.worktree)
|
|
16796
|
+
}, undefined, false, undefined, this)
|
|
16157
16797
|
]
|
|
16158
16798
|
}, undefined, true, undefined, this),
|
|
16159
|
-
/* @__PURE__ */ jsxDEV5("
|
|
16160
|
-
|
|
16799
|
+
/* @__PURE__ */ jsxDEV5("box", {
|
|
16800
|
+
style: { height: 1, flexDirection: "row" },
|
|
16161
16801
|
children: [
|
|
16162
|
-
|
|
16163
|
-
|
|
16802
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16803
|
+
fg: fleetTheme.faint,
|
|
16804
|
+
children: padFleetText("Project", 12)
|
|
16805
|
+
}, undefined, false, undefined, this),
|
|
16806
|
+
/* @__PURE__ */ jsxDEV5("text", {
|
|
16807
|
+
fg: fleetTheme.text,
|
|
16808
|
+
children: sanitizeFleetTerminalText(state.confirmDown.project)
|
|
16809
|
+
}, undefined, false, undefined, this)
|
|
16164
16810
|
]
|
|
16165
16811
|
}, undefined, true, undefined, this),
|
|
16166
16812
|
/* @__PURE__ */ jsxDEV5("box", {
|
|
@@ -16168,7 +16814,7 @@ function FleetApp({
|
|
|
16168
16814
|
}, undefined, false, undefined, this),
|
|
16169
16815
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
16170
16816
|
fg: fleetTheme.warning,
|
|
16171
|
-
children: "
|
|
16817
|
+
children: "Removes named volumes and project-built images (data loss)."
|
|
16172
16818
|
}, undefined, false, undefined, this),
|
|
16173
16819
|
/* @__PURE__ */ jsxDEV5("text", {
|
|
16174
16820
|
fg: fleetTheme.danger,
|
|
@@ -16179,15 +16825,33 @@ function FleetApp({
|
|
|
16179
16825
|
]
|
|
16180
16826
|
}, undefined, true, undefined, this);
|
|
16181
16827
|
}
|
|
16182
|
-
var LOG_RING_CAPACITY = 2000, EMPTY_CAPACITY;
|
|
16828
|
+
var LOG_RING_CAPACITY = 2000, TOAST_TTL_MS = 3500, EMPTY_CAPACITY, HELP_ROWS;
|
|
16183
16829
|
var init_FleetApp = __esm(() => {
|
|
16830
|
+
init_fleet_controller();
|
|
16184
16831
|
init_terminal_text();
|
|
16832
|
+
init_fleet_text();
|
|
16185
16833
|
init_fleet_theme();
|
|
16834
|
+
init_fleet_log_rows();
|
|
16835
|
+
init_fleet_format();
|
|
16836
|
+
init_fleet_status();
|
|
16186
16837
|
init_StackSidebar();
|
|
16187
16838
|
init_ServicePane();
|
|
16188
16839
|
init_LogPane();
|
|
16189
16840
|
init_FleetModal();
|
|
16190
16841
|
EMPTY_CAPACITY = { maxStacks: 0, live: 0, reserved: 0, queued: 0 };
|
|
16842
|
+
HELP_ROWS = [
|
|
16843
|
+
["j k \u2191\u2193", "move in focused pane \xB7 Tab cycles panes"],
|
|
16844
|
+
[", . [ ]", "previous / next stack \xB7 service"],
|
|
16845
|
+
["/", "filter stacks (Esc clears, then exits)"],
|
|
16846
|
+
["f \xB7 g G", "pause/resume follow \xB7 top / bottom of logs"],
|
|
16847
|
+
["o", "open selected endpoint in browser"],
|
|
16848
|
+
["y \xB7 Y", "yank endpoint URL \xB7 stack env block"],
|
|
16849
|
+
["c l p", "yank direct / local / public URL"],
|
|
16850
|
+
["s", "shared hostnames (claim, allow, deny, release)"],
|
|
16851
|
+
["D \xB7 d", "doctor report \xB7 down stack (removes volumes + images)"],
|
|
16852
|
+
["1 2 0", "split / stacked / auto layout"],
|
|
16853
|
+
["q", "quit \xB7 mouse click and wheel work everywhere"]
|
|
16854
|
+
];
|
|
16191
16855
|
});
|
|
16192
16856
|
|
|
16193
16857
|
// packages/tui/src/job-control.ts
|
|
@@ -16338,17 +17002,18 @@ __export(exports_src, {
|
|
|
16338
17002
|
var init_src3 = __esm(() => {
|
|
16339
17003
|
init_runtime();
|
|
16340
17004
|
init_fleet_source();
|
|
17005
|
+
init_fleet_controller();
|
|
16341
17006
|
init_terminal_text();
|
|
16342
17007
|
});
|
|
16343
17008
|
|
|
16344
17009
|
// packages/cli/src/index.ts
|
|
16345
17010
|
init_src2();
|
|
16346
17011
|
init_src();
|
|
16347
|
-
import { execFileSync as
|
|
17012
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
16348
17013
|
import { existsSync as existsSync24 } from "fs";
|
|
16349
|
-
import { dirname as dirname8, join as
|
|
17014
|
+
import { dirname as dirname8, join as join26 } from "path";
|
|
16350
17015
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
16351
|
-
var CLI_VERSION = "1.
|
|
17016
|
+
var CLI_VERSION = "1.3.0";
|
|
16352
17017
|
var PACKAGE_NAME = "@tridha643/hestia";
|
|
16353
17018
|
function compareVersions(a, b) {
|
|
16354
17019
|
const parse = (value) => {
|
|
@@ -16755,7 +17420,8 @@ usage:
|
|
|
16755
17420
|
health. Exit 1 only on error-level rows; never mutates anything
|
|
16756
17421
|
hestia stop <name> [--json] stop one supervised proc (idempotent)
|
|
16757
17422
|
hestia down [--destroy] [--project <name>] [--json]
|
|
16758
|
-
tear down procs + containers (--destroy also removes volumes
|
|
17423
|
+
tear down procs + containers (--destroy also removes volumes
|
|
17424
|
+
and project-built images);
|
|
16759
17425
|
--project works from the mirror after the worktree is deleted
|
|
16760
17426
|
hestia status [--json] show this worktree's stack
|
|
16761
17427
|
hestia env [--json] print the injected env (export lines)
|
|
@@ -16825,7 +17491,7 @@ async function main() {
|
|
|
16825
17491
|
`);
|
|
16826
17492
|
}
|
|
16827
17493
|
try {
|
|
16828
|
-
|
|
17494
|
+
execFileSync5("bun", installArgs, { stdio: flags.json ? "ignore" : "inherit" });
|
|
16829
17495
|
} catch (error) {
|
|
16830
17496
|
fail("upgrade-failed", `\`bun ${installArgs.join(" ")}\` failed: ${error.message}`, flags.json);
|
|
16831
17497
|
}
|
|
@@ -16842,8 +17508,8 @@ async function main() {
|
|
|
16842
17508
|
fail("usage", "usage: hestia skill path", flags.json);
|
|
16843
17509
|
const packageRoot = dirname8(dirname8(fileURLToPath3(import.meta.url)));
|
|
16844
17510
|
const candidates = [
|
|
16845
|
-
|
|
16846
|
-
|
|
17511
|
+
join26(packageRoot, "skills", "hestia", "SKILL.md"),
|
|
17512
|
+
join26(process.cwd(), "skills", "hestia", "SKILL.md")
|
|
16847
17513
|
];
|
|
16848
17514
|
const path = candidates.find(existsSync24);
|
|
16849
17515
|
if (path === undefined)
|
|
@@ -17378,8 +18044,8 @@ ${JSON.stringify(result.config, null, 2)}
|
|
|
17378
18044
|
process.stdout.write(JSON.stringify(line) + `
|
|
17379
18045
|
`);
|
|
17380
18046
|
} else {
|
|
17381
|
-
const
|
|
17382
|
-
process.stdout.write(`${line.service.padEnd(nameWidth)} \u2502 ${
|
|
18047
|
+
const text2 = line.meta ? `[hestia] ${line.text}` : line.text;
|
|
18048
|
+
process.stdout.write(`${line.service.padEnd(nameWidth)} \u2502 ${text2}
|
|
17383
18049
|
`);
|
|
17384
18050
|
}
|
|
17385
18051
|
}
|
|
@@ -17444,7 +18110,7 @@ ${JSON.stringify(result.config, null, 2)}
|
|
|
17444
18110
|
} else if (sub === "stop") {
|
|
17445
18111
|
if (launchdManagesThisHome()) {
|
|
17446
18112
|
const uid = process.getuid?.() ?? 501;
|
|
17447
|
-
|
|
18113
|
+
execFileSync5("launchctl", ["bootout", `gui/${uid}/dev.hestia.daemon`]);
|
|
17448
18114
|
process.stderr.write("warning: launchd agent booted out \u2014 `hestia daemon install` re-enables reboot revival\n");
|
|
17449
18115
|
} else {
|
|
17450
18116
|
await stopDaemonProcess();
|
|
@@ -17516,4 +18182,4 @@ export {
|
|
|
17516
18182
|
compareVersions
|
|
17517
18183
|
};
|
|
17518
18184
|
|
|
17519
|
-
//# debugId=
|
|
18185
|
+
//# debugId=3CDA6D645FAEF7DC64756E2164756E21
|