herdr-remote 0.2.11 → 0.2.13
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/dist/tui.mjs +373 -324
- package/herdr-plugin.toml +2 -2
- package/package.json +1 -1
- package/src/agent-status.js +114 -0
- package/src/herdr-api.js +123 -0
- package/src/herdr-command.js +72 -0
- package/src/herdr-plugin.js +9 -5
- package/src/host-connector.js +162 -1
- package/src/i18n/en.js +2 -0
- package/src/i18n/zh.js +2 -0
- package/src/service.js +7 -1
package/dist/tui.mjs
CHANGED
|
@@ -383,6 +383,324 @@ var require_config = __commonJS({
|
|
|
383
383
|
}
|
|
384
384
|
});
|
|
385
385
|
|
|
386
|
+
// src/updater.js
|
|
387
|
+
var require_updater = __commonJS({
|
|
388
|
+
"src/updater.js"(exports, module) {
|
|
389
|
+
"use strict";
|
|
390
|
+
var fs = __require("node:fs");
|
|
391
|
+
var os = __require("node:os");
|
|
392
|
+
var path = __require("node:path");
|
|
393
|
+
var { spawn } = __require("node:child_process");
|
|
394
|
+
var { PACKAGE_ROOT } = require_config();
|
|
395
|
+
var PACKAGE_NAME = "herdr-remote";
|
|
396
|
+
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
397
|
+
var MIRROR_REGISTRY = "https://registry.npmmirror.com";
|
|
398
|
+
function currentVersion() {
|
|
399
|
+
try {
|
|
400
|
+
return __require(path.join(PACKAGE_ROOT, "package.json")).version;
|
|
401
|
+
} catch {
|
|
402
|
+
return "0.0.0";
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function installKind2() {
|
|
406
|
+
const root = PACKAGE_ROOT;
|
|
407
|
+
if (!root.split(path.sep).includes("node_modules")) return "source";
|
|
408
|
+
try {
|
|
409
|
+
if (fs.lstatSync(root).isSymbolicLink()) return "linked";
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
return "npm";
|
|
413
|
+
}
|
|
414
|
+
function canSelfUpdate2() {
|
|
415
|
+
return installKind2() === "npm";
|
|
416
|
+
}
|
|
417
|
+
function normalizeRegistry(value) {
|
|
418
|
+
if (typeof value !== "string") return null;
|
|
419
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
420
|
+
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
421
|
+
return trimmed;
|
|
422
|
+
}
|
|
423
|
+
function registryFromNpmrc(filePath) {
|
|
424
|
+
let contents;
|
|
425
|
+
try {
|
|
426
|
+
contents = fs.readFileSync(filePath, "utf8");
|
|
427
|
+
} catch {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
let found = null;
|
|
431
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
432
|
+
const line = rawLine.trim();
|
|
433
|
+
if (!line || line.startsWith("#") || line.startsWith(";")) continue;
|
|
434
|
+
const match = /^registry\s*=\s*(.+)$/i.exec(line);
|
|
435
|
+
if (match) found = normalizeRegistry(match[1].replace(/^["']|["']$/g, ""));
|
|
436
|
+
}
|
|
437
|
+
return found;
|
|
438
|
+
}
|
|
439
|
+
function registryCandidates({ env = process.env, home = os.homedir(), cwd = process.cwd() } = {}) {
|
|
440
|
+
const candidates = [];
|
|
441
|
+
const add = (value) => {
|
|
442
|
+
const normalized = normalizeRegistry(value);
|
|
443
|
+
if (normalized && !candidates.includes(normalized)) candidates.push(normalized);
|
|
444
|
+
};
|
|
445
|
+
add(env.npm_config_registry);
|
|
446
|
+
add(env.NPM_CONFIG_REGISTRY);
|
|
447
|
+
add(env.HERDR_REMOTE_REGISTRY);
|
|
448
|
+
add(registryFromNpmrc(path.join(cwd, ".npmrc")));
|
|
449
|
+
add(registryFromNpmrc(path.join(home, ".npmrc")));
|
|
450
|
+
add(DEFAULT_REGISTRY);
|
|
451
|
+
add(MIRROR_REGISTRY);
|
|
452
|
+
return candidates;
|
|
453
|
+
}
|
|
454
|
+
function compareVersions(a, b) {
|
|
455
|
+
const parse = (value) => String(value).split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
456
|
+
const left = parse(a);
|
|
457
|
+
const right = parse(b);
|
|
458
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
459
|
+
const diff = (left[index] || 0) - (right[index] || 0);
|
|
460
|
+
if (diff > 0) return 1;
|
|
461
|
+
if (diff < 0) return -1;
|
|
462
|
+
}
|
|
463
|
+
return 0;
|
|
464
|
+
}
|
|
465
|
+
async function askRegistry(registry, { timeoutMs, fetchImpl }) {
|
|
466
|
+
const controller = new AbortController();
|
|
467
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
468
|
+
try {
|
|
469
|
+
const response = await fetchImpl(`${registry}/${PACKAGE_NAME}/latest`, {
|
|
470
|
+
signal: controller.signal,
|
|
471
|
+
headers: { Accept: "application/vnd.npm.install-v1+json" }
|
|
472
|
+
});
|
|
473
|
+
if (!response.ok) return { ok: false, message: `HTTP ${response.status ?? "?"}` };
|
|
474
|
+
const body = await response.json();
|
|
475
|
+
const latest = typeof body?.version === "string" ? body.version : null;
|
|
476
|
+
if (!latest) return { ok: false, message: "registry answered without a version" };
|
|
477
|
+
return { ok: true, latest };
|
|
478
|
+
} catch (error) {
|
|
479
|
+
return { ok: false, message: error?.name === "AbortError" ? "timed out" : String(error?.message || error) };
|
|
480
|
+
} finally {
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function checkForUpdate2({
|
|
485
|
+
timeoutMs = 6e3,
|
|
486
|
+
fetchImpl = globalThis.fetch,
|
|
487
|
+
registries = registryCandidates()
|
|
488
|
+
} = {}) {
|
|
489
|
+
const current = currentVersion();
|
|
490
|
+
const failures = [];
|
|
491
|
+
for (const registry of registries) {
|
|
492
|
+
const attempt = await askRegistry(registry, { timeoutMs, fetchImpl });
|
|
493
|
+
if (attempt.ok) {
|
|
494
|
+
return {
|
|
495
|
+
ok: true,
|
|
496
|
+
current,
|
|
497
|
+
latest: attempt.latest,
|
|
498
|
+
registry,
|
|
499
|
+
updateAvailable: compareVersions(attempt.latest, current) > 0
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
failures.push(`${registry}: ${attempt.message}`);
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
ok: false,
|
|
506
|
+
current,
|
|
507
|
+
errorKey: "update.errorNetwork",
|
|
508
|
+
message: failures.join("; "),
|
|
509
|
+
triedRegistries: registries
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function performUpdate2({
|
|
513
|
+
spawnImpl = spawn,
|
|
514
|
+
timeoutMs = 18e4,
|
|
515
|
+
// Seam for tests: the suite runs from a checkout, where the guard below is
|
|
516
|
+
// correctly the only reachable outcome.
|
|
517
|
+
installKindImpl = installKind2,
|
|
518
|
+
// The registry that just answered the version check. Installing from
|
|
519
|
+
// somewhere the machine has been shown to reach beats installing from a
|
|
520
|
+
// default it may have no route to. Empty means "whatever npm is configured
|
|
521
|
+
// with", which is the right answer when no check has run.
|
|
522
|
+
registry = ""
|
|
523
|
+
} = {}) {
|
|
524
|
+
return new Promise((resolve) => {
|
|
525
|
+
const kind = installKindImpl();
|
|
526
|
+
if (kind !== "npm") {
|
|
527
|
+
resolve({ ok: false, errorKey: `update.cannot.${kind}` });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const args = ["install", "-g", `${PACKAGE_NAME}@latest`];
|
|
531
|
+
const normalizedRegistry = normalizeRegistry(registry);
|
|
532
|
+
if (normalizedRegistry) args.push("--registry", normalizedRegistry);
|
|
533
|
+
const child = spawnImpl("npm", args, {
|
|
534
|
+
encoding: "utf8",
|
|
535
|
+
timeout: timeoutMs
|
|
536
|
+
});
|
|
537
|
+
let output = "";
|
|
538
|
+
const collect = (chunk) => {
|
|
539
|
+
output += String(chunk);
|
|
540
|
+
};
|
|
541
|
+
child.stdout?.on("data", collect);
|
|
542
|
+
child.stderr?.on("data", collect);
|
|
543
|
+
child.on("error", (error) => {
|
|
544
|
+
resolve({ ok: false, errorKey: "update.errorFailed", output: error.message });
|
|
545
|
+
});
|
|
546
|
+
child.on("close", (code) => {
|
|
547
|
+
resolve({ ok: code === 0, output: output.trim().slice(-2e3) });
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
module.exports = {
|
|
552
|
+
PACKAGE_NAME,
|
|
553
|
+
DEFAULT_REGISTRY,
|
|
554
|
+
MIRROR_REGISTRY,
|
|
555
|
+
canSelfUpdate: canSelfUpdate2,
|
|
556
|
+
checkForUpdate: checkForUpdate2,
|
|
557
|
+
compareVersions,
|
|
558
|
+
currentVersion,
|
|
559
|
+
installKind: installKind2,
|
|
560
|
+
performUpdate: performUpdate2,
|
|
561
|
+
registryCandidates
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/herdr-command.js
|
|
567
|
+
var require_herdr_command = __commonJS({
|
|
568
|
+
"src/herdr-command.js"(exports, module) {
|
|
569
|
+
"use strict";
|
|
570
|
+
var fs = __require("node:fs");
|
|
571
|
+
var os = __require("node:os");
|
|
572
|
+
var path = __require("node:path");
|
|
573
|
+
var { spawnSync } = __require("node:child_process");
|
|
574
|
+
var { compareVersions } = require_updater();
|
|
575
|
+
var COMMAND_NAME = "herdr";
|
|
576
|
+
var MIN_HERDR_VERSION2 = "0.9.1";
|
|
577
|
+
var FALLBACK_DIRECTORIES = [
|
|
578
|
+
"~/.local/bin",
|
|
579
|
+
"~/.cargo/bin",
|
|
580
|
+
"~/bin",
|
|
581
|
+
"/opt/homebrew/bin",
|
|
582
|
+
"/usr/local/bin",
|
|
583
|
+
"/home/linuxbrew/.linuxbrew/bin",
|
|
584
|
+
"/usr/bin"
|
|
585
|
+
];
|
|
586
|
+
function expandHome(directory, home) {
|
|
587
|
+
if (directory === "~") return home;
|
|
588
|
+
if (directory.startsWith("~/")) return path.join(home, directory.slice(2));
|
|
589
|
+
return directory;
|
|
590
|
+
}
|
|
591
|
+
function candidateNames(base = COMMAND_NAME) {
|
|
592
|
+
return process.platform === "win32" ? [base, `${base}.exe`, `${base}.cmd`, `${base}.bat`] : [base];
|
|
593
|
+
}
|
|
594
|
+
function isExecutableFile(candidate) {
|
|
595
|
+
try {
|
|
596
|
+
if (!fs.statSync(candidate).isFile()) return false;
|
|
597
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
598
|
+
return true;
|
|
599
|
+
} catch {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function findInDirectory(directory, names = candidateNames()) {
|
|
604
|
+
if (!directory) return null;
|
|
605
|
+
for (const name of names) {
|
|
606
|
+
const candidate = path.join(directory, name);
|
|
607
|
+
if (isExecutableFile(candidate)) return candidate;
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
function findOnSearchPath(searchPath, names = candidateNames()) {
|
|
612
|
+
for (const entry of String(searchPath || "").split(path.delimiter)) {
|
|
613
|
+
const found = findInDirectory(entry.trim(), names);
|
|
614
|
+
if (found) return found;
|
|
615
|
+
}
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
function fallbackDirectories(home = os.homedir()) {
|
|
619
|
+
return FALLBACK_DIRECTORIES.map((directory) => expandHome(directory, home));
|
|
620
|
+
}
|
|
621
|
+
function looksLikePath(value) {
|
|
622
|
+
return value.includes("/") || value.includes(path.sep);
|
|
623
|
+
}
|
|
624
|
+
function resolveOverride(value, searchPath) {
|
|
625
|
+
const trimmed = String(value || "").trim();
|
|
626
|
+
if (!trimmed) return null;
|
|
627
|
+
if (looksLikePath(trimmed)) {
|
|
628
|
+
const absolute = path.resolve(trimmed);
|
|
629
|
+
if (isExecutableFile(absolute)) return absolute;
|
|
630
|
+
return findInDirectory(absolute);
|
|
631
|
+
}
|
|
632
|
+
return findOnSearchPath(searchPath, candidateNames(trimmed));
|
|
633
|
+
}
|
|
634
|
+
function findHerdrCommand({ env = process.env, home = os.homedir(), directories = fallbackDirectories(home) } = {}) {
|
|
635
|
+
const override = resolveOverride(env.HERDR_BIN_PATH, env.PATH);
|
|
636
|
+
if (override) return { command: override, source: "env", found: true };
|
|
637
|
+
const onPath = findOnSearchPath(env.PATH);
|
|
638
|
+
if (onPath) return { command: onPath, source: "path", found: true };
|
|
639
|
+
for (const directory of directories) {
|
|
640
|
+
const found = findInDirectory(directory);
|
|
641
|
+
if (found) return { command: found, source: "fallback", found: true };
|
|
642
|
+
}
|
|
643
|
+
return { command: COMMAND_NAME, source: "unresolved", found: false };
|
|
644
|
+
}
|
|
645
|
+
function resolveHerdrCommand(options) {
|
|
646
|
+
return findHerdrCommand(options).command;
|
|
647
|
+
}
|
|
648
|
+
function verifyHerdrCommand(command, options = {}) {
|
|
649
|
+
if (typeof command === "string" && looksLikePath(command) && isExecutableFile(command)) {
|
|
650
|
+
return { command, source: "verified", found: true };
|
|
651
|
+
}
|
|
652
|
+
return findHerdrCommand(options);
|
|
653
|
+
}
|
|
654
|
+
function parseHerdrVersion(output) {
|
|
655
|
+
const match = /(\d+)\.(\d+)\.(\d+)/.exec(String(output || ""));
|
|
656
|
+
return match ? `${match[1]}.${match[2]}.${match[3]}` : null;
|
|
657
|
+
}
|
|
658
|
+
function meetsMinimum(version, minimum = MIN_HERDR_VERSION2) {
|
|
659
|
+
if (!version) return true;
|
|
660
|
+
return compareVersions(version, minimum) >= 0;
|
|
661
|
+
}
|
|
662
|
+
function herdrVersion({ command, timeout = 1e4, ...lookup } = {}) {
|
|
663
|
+
const resolved = command || resolveHerdrCommand(lookup);
|
|
664
|
+
const unknown = { command: resolved, version: null, raw: null, supported: true, ok: false };
|
|
665
|
+
let result;
|
|
666
|
+
try {
|
|
667
|
+
result = spawnSync(resolved, ["--version"], { encoding: "utf8", timeout });
|
|
668
|
+
} catch {
|
|
669
|
+
return unknown;
|
|
670
|
+
}
|
|
671
|
+
if (result.error || result.status !== 0 && result.status !== 1) return unknown;
|
|
672
|
+
const raw = `${result.stdout || ""}${result.stderr || ""}`.trim();
|
|
673
|
+
const version = parseHerdrVersion(raw);
|
|
674
|
+
return { command: resolved, version, raw: raw || null, supported: meetsMinimum(version), ok: true };
|
|
675
|
+
}
|
|
676
|
+
function herdrOutdatedMessage(version) {
|
|
677
|
+
return `Herdr ${version} is older than ${MIN_HERDR_VERSION2}, which herdr-remote is written against. Run "herdr update" \u2014 window titles, background machine activation and large pastes all misbehave in browser windows before that release.`;
|
|
678
|
+
}
|
|
679
|
+
function herdrNotFoundMessage({ env = process.env, home = os.homedir(), directories = fallbackDirectories(home) } = {}) {
|
|
680
|
+
const override = String(env.HERDR_BIN_PATH || "").trim();
|
|
681
|
+
const parts = [`Herdr executable "${COMMAND_NAME}" was not found.`];
|
|
682
|
+
if (override) parts.push(`HERDR_BIN_PATH=${override} does not point at an executable.`);
|
|
683
|
+
parts.push(directories.length ? `Searched PATH and ${directories.join(", ")}.` : "Searched PATH.");
|
|
684
|
+
parts.push("Install Herdr or set HERDR_BIN_PATH to its full path, then restart herdr-remote.");
|
|
685
|
+
return parts.join(" ");
|
|
686
|
+
}
|
|
687
|
+
module.exports = {
|
|
688
|
+
COMMAND_NAME,
|
|
689
|
+
FALLBACK_DIRECTORIES,
|
|
690
|
+
MIN_HERDR_VERSION: MIN_HERDR_VERSION2,
|
|
691
|
+
fallbackDirectories,
|
|
692
|
+
findHerdrCommand,
|
|
693
|
+
herdrNotFoundMessage,
|
|
694
|
+
herdrOutdatedMessage,
|
|
695
|
+
herdrVersion,
|
|
696
|
+
meetsMinimum,
|
|
697
|
+
parseHerdrVersion,
|
|
698
|
+
resolveHerdrCommand,
|
|
699
|
+
verifyHerdrCommand
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
|
|
386
704
|
// src/i18n/en.js
|
|
387
705
|
var require_en = __commonJS({
|
|
388
706
|
"src/i18n/en.js"(exports, module) {
|
|
@@ -530,6 +848,8 @@ var require_en = __commonJS({
|
|
|
530
848
|
"herdr.unregisterDone": "Plugin unregistered.",
|
|
531
849
|
"herdr.registerFailed": "Registration failed: {message}",
|
|
532
850
|
"herdr.cliMissing": "Command herdr not found. Set HERDR_BIN_PATH to its full path.",
|
|
851
|
+
"herdr.version": "Herdr version",
|
|
852
|
+
"herdr.versionOutdated": 'Herdr {version} is older than {minimum}. Run "herdr update": window titles, background machine activation and large pastes misbehave in browser windows before that release.',
|
|
533
853
|
"about.title": "Language & about",
|
|
534
854
|
"about.language": "Interface language",
|
|
535
855
|
"about.languageAuto": "Follow system ({detected})",
|
|
@@ -755,6 +1075,8 @@ var require_zh = __commonJS({
|
|
|
755
1075
|
"herdr.unregisterDone": "\u5DF2\u53D6\u6D88\u6CE8\u518C\u3002",
|
|
756
1076
|
"herdr.registerFailed": "\u6CE8\u518C\u5931\u8D25\uFF1A{message}",
|
|
757
1077
|
"herdr.cliMissing": "\u672A\u627E\u5230 herdr \u547D\u4EE4\uFF0C\u53EF\u5C06 HERDR_BIN_PATH \u8BBE\u4E3A\u5176\u5B8C\u6574\u8DEF\u5F84\u3002",
|
|
1078
|
+
"herdr.version": "Herdr \u7248\u672C",
|
|
1079
|
+
"herdr.versionOutdated": "Herdr {version} \u4F4E\u4E8E {minimum}\uFF0C\u8BF7\u8FD0\u884C \u201Cherdr update\u201D\uFF1A\u5728\u8BE5\u7248\u672C\u4E4B\u524D\uFF0C\u6D4F\u89C8\u5668\u7A97\u53E3\u7684\u6807\u9898\u3001\u540E\u53F0\u673A\u5668\u6FC0\u6D3B\u4E0E\u5927\u6BB5\u7C98\u8D34\u90FD\u4F1A\u51FA\u95EE\u9898\u3002",
|
|
758
1080
|
"about.title": "\u8BED\u8A00\u4E0E\u5173\u4E8E",
|
|
759
1081
|
"about.language": "\u754C\u9762\u8BED\u8A00",
|
|
760
1082
|
"about.languageAuto": "\u8DDF\u968F\u7CFB\u7EDF\uFF08{detected}\uFF09",
|
|
@@ -1416,149 +1738,44 @@ var require_socket_discovery = __commonJS({
|
|
|
1416
1738
|
const appData = env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
1417
1739
|
return path.join(appData, "herdr", "herdr.sock");
|
|
1418
1740
|
}
|
|
1419
|
-
const configHome = env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
1420
|
-
return path.join(configHome, "herdr", "herdr.sock");
|
|
1421
|
-
}
|
|
1422
|
-
function resolveSocketPath(configuredPath = null, env = process.env) {
|
|
1423
|
-
return configuredPath || env.HERDR_SOCKET_PATH || defaultSocketPath(env);
|
|
1424
|
-
}
|
|
1425
|
-
function inspectSocket(socketPath, options = {}) {
|
|
1426
|
-
if (typeof socketPath !== "string" || socketPath.length === 0) {
|
|
1427
|
-
return { ok: false, reason: "socket path is empty", path: socketPath };
|
|
1428
|
-
}
|
|
1429
|
-
try {
|
|
1430
|
-
const stat = fs.statSync(socketPath);
|
|
1431
|
-
if (process.platform !== "win32" && !stat.isSocket()) {
|
|
1432
|
-
return { ok: false, reason: "path is not a Unix socket", path: socketPath };
|
|
1433
|
-
}
|
|
1434
|
-
if (options.requireOwner !== false && typeof process.getuid === "function" && stat.uid !== process.getuid()) {
|
|
1435
|
-
return { ok: false, reason: "socket is not owned by the current user", path: socketPath, uid: stat.uid };
|
|
1436
|
-
}
|
|
1437
|
-
return { ok: true, path: socketPath, uid: stat.uid, mode: stat.mode };
|
|
1438
|
-
} catch (error) {
|
|
1439
|
-
return { ok: false, reason: error.code === "ENOENT" ? "socket does not exist" : error.message, path: socketPath };
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
function assertSocket(socketPath, options = {}) {
|
|
1443
|
-
const result = inspectSocket(socketPath, options);
|
|
1444
|
-
if (!result.ok) {
|
|
1445
|
-
const error = new Error(`Herdr socket unavailable at ${socketPath}: ${result.reason}`);
|
|
1446
|
-
error.code = "HERDR_SOCKET_UNAVAILABLE";
|
|
1447
|
-
error.details = result;
|
|
1448
|
-
throw error;
|
|
1449
|
-
}
|
|
1450
|
-
return result;
|
|
1451
|
-
}
|
|
1452
|
-
module.exports = {
|
|
1453
|
-
defaultSocketPath,
|
|
1454
|
-
resolveSocketPath,
|
|
1455
|
-
inspectSocket,
|
|
1456
|
-
assertSocket
|
|
1457
|
-
};
|
|
1458
|
-
}
|
|
1459
|
-
});
|
|
1460
|
-
|
|
1461
|
-
// src/herdr-command.js
|
|
1462
|
-
var require_herdr_command = __commonJS({
|
|
1463
|
-
"src/herdr-command.js"(exports, module) {
|
|
1464
|
-
"use strict";
|
|
1465
|
-
var fs = __require("node:fs");
|
|
1466
|
-
var os = __require("node:os");
|
|
1467
|
-
var path = __require("node:path");
|
|
1468
|
-
var COMMAND_NAME = "herdr";
|
|
1469
|
-
var FALLBACK_DIRECTORIES = [
|
|
1470
|
-
"~/.local/bin",
|
|
1471
|
-
"~/.cargo/bin",
|
|
1472
|
-
"~/bin",
|
|
1473
|
-
"/opt/homebrew/bin",
|
|
1474
|
-
"/usr/local/bin",
|
|
1475
|
-
"/home/linuxbrew/.linuxbrew/bin",
|
|
1476
|
-
"/usr/bin"
|
|
1477
|
-
];
|
|
1478
|
-
function expandHome(directory, home) {
|
|
1479
|
-
if (directory === "~") return home;
|
|
1480
|
-
if (directory.startsWith("~/")) return path.join(home, directory.slice(2));
|
|
1481
|
-
return directory;
|
|
1482
|
-
}
|
|
1483
|
-
function candidateNames(base = COMMAND_NAME) {
|
|
1484
|
-
return process.platform === "win32" ? [base, `${base}.exe`, `${base}.cmd`, `${base}.bat`] : [base];
|
|
1485
|
-
}
|
|
1486
|
-
function isExecutableFile(candidate) {
|
|
1487
|
-
try {
|
|
1488
|
-
if (!fs.statSync(candidate).isFile()) return false;
|
|
1489
|
-
fs.accessSync(candidate, fs.constants.X_OK);
|
|
1490
|
-
return true;
|
|
1491
|
-
} catch {
|
|
1492
|
-
return false;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
function findInDirectory(directory, names = candidateNames()) {
|
|
1496
|
-
if (!directory) return null;
|
|
1497
|
-
for (const name of names) {
|
|
1498
|
-
const candidate = path.join(directory, name);
|
|
1499
|
-
if (isExecutableFile(candidate)) return candidate;
|
|
1500
|
-
}
|
|
1501
|
-
return null;
|
|
1502
|
-
}
|
|
1503
|
-
function findOnSearchPath(searchPath, names = candidateNames()) {
|
|
1504
|
-
for (const entry of String(searchPath || "").split(path.delimiter)) {
|
|
1505
|
-
const found = findInDirectory(entry.trim(), names);
|
|
1506
|
-
if (found) return found;
|
|
1507
|
-
}
|
|
1508
|
-
return null;
|
|
1509
|
-
}
|
|
1510
|
-
function fallbackDirectories(home = os.homedir()) {
|
|
1511
|
-
return FALLBACK_DIRECTORIES.map((directory) => expandHome(directory, home));
|
|
1741
|
+
const configHome = env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
1742
|
+
return path.join(configHome, "herdr", "herdr.sock");
|
|
1512
1743
|
}
|
|
1513
|
-
function
|
|
1514
|
-
return
|
|
1744
|
+
function resolveSocketPath(configuredPath = null, env = process.env) {
|
|
1745
|
+
return configuredPath || env.HERDR_SOCKET_PATH || defaultSocketPath(env);
|
|
1515
1746
|
}
|
|
1516
|
-
function
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (looksLikePath(trimmed)) {
|
|
1520
|
-
const absolute = path.resolve(trimmed);
|
|
1521
|
-
if (isExecutableFile(absolute)) return absolute;
|
|
1522
|
-
return findInDirectory(absolute);
|
|
1747
|
+
function inspectSocket(socketPath, options = {}) {
|
|
1748
|
+
if (typeof socketPath !== "string" || socketPath.length === 0) {
|
|
1749
|
+
return { ok: false, reason: "socket path is empty", path: socketPath };
|
|
1523
1750
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1751
|
+
try {
|
|
1752
|
+
const stat = fs.statSync(socketPath);
|
|
1753
|
+
if (process.platform !== "win32" && !stat.isSocket()) {
|
|
1754
|
+
return { ok: false, reason: "path is not a Unix socket", path: socketPath };
|
|
1755
|
+
}
|
|
1756
|
+
if (options.requireOwner !== false && typeof process.getuid === "function" && stat.uid !== process.getuid()) {
|
|
1757
|
+
return { ok: false, reason: "socket is not owned by the current user", path: socketPath, uid: stat.uid };
|
|
1758
|
+
}
|
|
1759
|
+
return { ok: true, path: socketPath, uid: stat.uid, mode: stat.mode };
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
return { ok: false, reason: error.code === "ENOENT" ? "socket does not exist" : error.message, path: socketPath };
|
|
1534
1762
|
}
|
|
1535
|
-
return { command: COMMAND_NAME, source: "unresolved", found: false };
|
|
1536
|
-
}
|
|
1537
|
-
function resolveHerdrCommand(options) {
|
|
1538
|
-
return findHerdrCommand(options).command;
|
|
1539
1763
|
}
|
|
1540
|
-
function
|
|
1541
|
-
|
|
1542
|
-
|
|
1764
|
+
function assertSocket(socketPath, options = {}) {
|
|
1765
|
+
const result = inspectSocket(socketPath, options);
|
|
1766
|
+
if (!result.ok) {
|
|
1767
|
+
const error = new Error(`Herdr socket unavailable at ${socketPath}: ${result.reason}`);
|
|
1768
|
+
error.code = "HERDR_SOCKET_UNAVAILABLE";
|
|
1769
|
+
error.details = result;
|
|
1770
|
+
throw error;
|
|
1543
1771
|
}
|
|
1544
|
-
return
|
|
1545
|
-
}
|
|
1546
|
-
function herdrNotFoundMessage({ env = process.env, home = os.homedir(), directories = fallbackDirectories(home) } = {}) {
|
|
1547
|
-
const override = String(env.HERDR_BIN_PATH || "").trim();
|
|
1548
|
-
const parts = [`Herdr executable "${COMMAND_NAME}" was not found.`];
|
|
1549
|
-
if (override) parts.push(`HERDR_BIN_PATH=${override} does not point at an executable.`);
|
|
1550
|
-
parts.push(directories.length ? `Searched PATH and ${directories.join(", ")}.` : "Searched PATH.");
|
|
1551
|
-
parts.push("Install Herdr or set HERDR_BIN_PATH to its full path, then restart herdr-remote.");
|
|
1552
|
-
return parts.join(" ");
|
|
1772
|
+
return result;
|
|
1553
1773
|
}
|
|
1554
1774
|
module.exports = {
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
herdrNotFoundMessage,
|
|
1560
|
-
resolveHerdrCommand,
|
|
1561
|
-
verifyHerdrCommand
|
|
1775
|
+
defaultSocketPath,
|
|
1776
|
+
resolveSocketPath,
|
|
1777
|
+
inspectSocket,
|
|
1778
|
+
assertSocket
|
|
1562
1779
|
};
|
|
1563
1780
|
}
|
|
1564
1781
|
});
|
|
@@ -1593,7 +1810,7 @@ var require_service = __commonJS({
|
|
|
1593
1810
|
} = require_terminal_palette();
|
|
1594
1811
|
var { resolveSocketPath } = require_socket_discovery();
|
|
1595
1812
|
var { preferredLanAddress: preferredLanAddress2 } = require_net_interfaces();
|
|
1596
|
-
var { findHerdrCommand, resolveHerdrCommand } = require_herdr_command();
|
|
1813
|
+
var { MIN_HERDR_VERSION: MIN_HERDR_VERSION2, findHerdrCommand, herdrVersion, resolveHerdrCommand } = require_herdr_command();
|
|
1597
1814
|
var RUNTIME_VERSION = 2;
|
|
1598
1815
|
function pidAlive(pid) {
|
|
1599
1816
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -1900,6 +2117,7 @@ var require_service = __commonJS({
|
|
|
1900
2117
|
}
|
|
1901
2118
|
const socketPath = resolveSocketPath(config.herdr.socketPath);
|
|
1902
2119
|
const herdr = findHerdrCommand();
|
|
2120
|
+
const installed = herdr.found ? herdrVersion({ command: herdr.command }) : { version: null, supported: true };
|
|
1903
2121
|
return {
|
|
1904
2122
|
ok: true,
|
|
1905
2123
|
mode: config.relay.mode,
|
|
@@ -1920,7 +2138,10 @@ var require_service = __commonJS({
|
|
|
1920
2138
|
socketExists: Boolean(socketPath) && fs.existsSync(socketPath),
|
|
1921
2139
|
herdrCommand: herdr.command,
|
|
1922
2140
|
herdrCommandFound: herdr.found,
|
|
1923
|
-
herdrCommandSource: herdr.source
|
|
2141
|
+
herdrCommandSource: herdr.source,
|
|
2142
|
+
herdrVersion: installed.version,
|
|
2143
|
+
herdrVersionSupported: installed.supported,
|
|
2144
|
+
herdrVersionMinimum: MIN_HERDR_VERSION2
|
|
1924
2145
|
},
|
|
1925
2146
|
publicUrl: resolvePublicUrl2(config, lanAddress),
|
|
1926
2147
|
startedAt: state.startedAt || null
|
|
@@ -2400,15 +2621,14 @@ var require_herdr_plugin = __commonJS({
|
|
|
2400
2621
|
var path = __require("node:path");
|
|
2401
2622
|
var { spawnSync } = __require("node:child_process");
|
|
2402
2623
|
var { PACKAGE_ROOT } = require_config();
|
|
2403
|
-
var { resolveHerdrCommand } = require_herdr_command();
|
|
2624
|
+
var { herdrVersion, resolveHerdrCommand } = require_herdr_command();
|
|
2404
2625
|
var PLUGIN_ID = "herdr.remote.web";
|
|
2405
2626
|
var MANIFEST_NAME = "herdr-plugin.toml";
|
|
2406
2627
|
function manifestPath() {
|
|
2407
2628
|
return path.join(PACKAGE_ROOT, MANIFEST_NAME);
|
|
2408
2629
|
}
|
|
2409
2630
|
function herdrAvailable() {
|
|
2410
|
-
|
|
2411
|
-
return result.status === 0 || result.status === 1;
|
|
2631
|
+
return herdrVersion().ok;
|
|
2412
2632
|
}
|
|
2413
2633
|
function runHerdr(args, { timeout = 15e3 } = {}) {
|
|
2414
2634
|
const result = spawnSync(resolveHerdrCommand(), args, { encoding: "utf8", timeout });
|
|
@@ -2441,6 +2661,8 @@ var require_herdr_plugin = __commonJS({
|
|
|
2441
2661
|
if (!fs.existsSync(manifestPath())) {
|
|
2442
2662
|
return { available: false, registered: false, reason: "manifest missing" };
|
|
2443
2663
|
}
|
|
2664
|
+
const { version, supported } = herdrVersion();
|
|
2665
|
+
const installed = { version, versionSupported: supported };
|
|
2444
2666
|
let result;
|
|
2445
2667
|
try {
|
|
2446
2668
|
result = runHerdr(["plugin", "list"]);
|
|
@@ -2449,11 +2671,11 @@ var require_herdr_plugin = __commonJS({
|
|
|
2449
2671
|
throw error;
|
|
2450
2672
|
}
|
|
2451
2673
|
if (result.status !== 0) {
|
|
2452
|
-
return { available: true, registered: false, reason: String(result.stderr || "").trim() };
|
|
2674
|
+
return { available: true, registered: false, reason: String(result.stderr || "").trim(), ...installed };
|
|
2453
2675
|
}
|
|
2454
2676
|
const plugins = parsePluginList(result.stdout);
|
|
2455
2677
|
const entry = plugins.find((plugin) => plugin.id === PLUGIN_ID);
|
|
2456
|
-
if (!entry) return { available: true, registered: false, packageRoot: PACKAGE_ROOT };
|
|
2678
|
+
if (!entry) return { available: true, registered: false, packageRoot: PACKAGE_ROOT, ...installed };
|
|
2457
2679
|
return {
|
|
2458
2680
|
available: true,
|
|
2459
2681
|
registered: true,
|
|
@@ -2462,7 +2684,8 @@ var require_herdr_plugin = __commonJS({
|
|
|
2462
2684
|
// A stale link pointing at an old checkout is the main failure mode after
|
|
2463
2685
|
// switching from a source install to npm.
|
|
2464
2686
|
stale: Boolean(entry.localPath) && path.resolve(entry.localPath) !== path.resolve(PACKAGE_ROOT),
|
|
2465
|
-
packageRoot: PACKAGE_ROOT
|
|
2687
|
+
packageRoot: PACKAGE_ROOT,
|
|
2688
|
+
...installed
|
|
2466
2689
|
};
|
|
2467
2690
|
}
|
|
2468
2691
|
function register() {
|
|
@@ -2495,186 +2718,6 @@ var require_herdr_plugin = __commonJS({
|
|
|
2495
2718
|
}
|
|
2496
2719
|
});
|
|
2497
2720
|
|
|
2498
|
-
// src/updater.js
|
|
2499
|
-
var require_updater = __commonJS({
|
|
2500
|
-
"src/updater.js"(exports, module) {
|
|
2501
|
-
"use strict";
|
|
2502
|
-
var fs = __require("node:fs");
|
|
2503
|
-
var os = __require("node:os");
|
|
2504
|
-
var path = __require("node:path");
|
|
2505
|
-
var { spawn } = __require("node:child_process");
|
|
2506
|
-
var { PACKAGE_ROOT } = require_config();
|
|
2507
|
-
var PACKAGE_NAME = "herdr-remote";
|
|
2508
|
-
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
2509
|
-
var MIRROR_REGISTRY = "https://registry.npmmirror.com";
|
|
2510
|
-
function currentVersion() {
|
|
2511
|
-
try {
|
|
2512
|
-
return __require(path.join(PACKAGE_ROOT, "package.json")).version;
|
|
2513
|
-
} catch {
|
|
2514
|
-
return "0.0.0";
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
function installKind2() {
|
|
2518
|
-
const root = PACKAGE_ROOT;
|
|
2519
|
-
if (!root.split(path.sep).includes("node_modules")) return "source";
|
|
2520
|
-
try {
|
|
2521
|
-
if (fs.lstatSync(root).isSymbolicLink()) return "linked";
|
|
2522
|
-
} catch {
|
|
2523
|
-
}
|
|
2524
|
-
return "npm";
|
|
2525
|
-
}
|
|
2526
|
-
function canSelfUpdate2() {
|
|
2527
|
-
return installKind2() === "npm";
|
|
2528
|
-
}
|
|
2529
|
-
function normalizeRegistry(value) {
|
|
2530
|
-
if (typeof value !== "string") return null;
|
|
2531
|
-
const trimmed = value.trim().replace(/\/+$/, "");
|
|
2532
|
-
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
2533
|
-
return trimmed;
|
|
2534
|
-
}
|
|
2535
|
-
function registryFromNpmrc(filePath) {
|
|
2536
|
-
let contents;
|
|
2537
|
-
try {
|
|
2538
|
-
contents = fs.readFileSync(filePath, "utf8");
|
|
2539
|
-
} catch {
|
|
2540
|
-
return null;
|
|
2541
|
-
}
|
|
2542
|
-
let found = null;
|
|
2543
|
-
for (const rawLine of contents.split(/\r?\n/)) {
|
|
2544
|
-
const line = rawLine.trim();
|
|
2545
|
-
if (!line || line.startsWith("#") || line.startsWith(";")) continue;
|
|
2546
|
-
const match = /^registry\s*=\s*(.+)$/i.exec(line);
|
|
2547
|
-
if (match) found = normalizeRegistry(match[1].replace(/^["']|["']$/g, ""));
|
|
2548
|
-
}
|
|
2549
|
-
return found;
|
|
2550
|
-
}
|
|
2551
|
-
function registryCandidates({ env = process.env, home = os.homedir(), cwd = process.cwd() } = {}) {
|
|
2552
|
-
const candidates = [];
|
|
2553
|
-
const add = (value) => {
|
|
2554
|
-
const normalized = normalizeRegistry(value);
|
|
2555
|
-
if (normalized && !candidates.includes(normalized)) candidates.push(normalized);
|
|
2556
|
-
};
|
|
2557
|
-
add(env.npm_config_registry);
|
|
2558
|
-
add(env.NPM_CONFIG_REGISTRY);
|
|
2559
|
-
add(env.HERDR_REMOTE_REGISTRY);
|
|
2560
|
-
add(registryFromNpmrc(path.join(cwd, ".npmrc")));
|
|
2561
|
-
add(registryFromNpmrc(path.join(home, ".npmrc")));
|
|
2562
|
-
add(DEFAULT_REGISTRY);
|
|
2563
|
-
add(MIRROR_REGISTRY);
|
|
2564
|
-
return candidates;
|
|
2565
|
-
}
|
|
2566
|
-
function compareVersions(a, b) {
|
|
2567
|
-
const parse = (value) => String(value).split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
2568
|
-
const left = parse(a);
|
|
2569
|
-
const right = parse(b);
|
|
2570
|
-
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
2571
|
-
const diff = (left[index] || 0) - (right[index] || 0);
|
|
2572
|
-
if (diff > 0) return 1;
|
|
2573
|
-
if (diff < 0) return -1;
|
|
2574
|
-
}
|
|
2575
|
-
return 0;
|
|
2576
|
-
}
|
|
2577
|
-
async function askRegistry(registry, { timeoutMs, fetchImpl }) {
|
|
2578
|
-
const controller = new AbortController();
|
|
2579
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2580
|
-
try {
|
|
2581
|
-
const response = await fetchImpl(`${registry}/${PACKAGE_NAME}/latest`, {
|
|
2582
|
-
signal: controller.signal,
|
|
2583
|
-
headers: { Accept: "application/vnd.npm.install-v1+json" }
|
|
2584
|
-
});
|
|
2585
|
-
if (!response.ok) return { ok: false, message: `HTTP ${response.status ?? "?"}` };
|
|
2586
|
-
const body = await response.json();
|
|
2587
|
-
const latest = typeof body?.version === "string" ? body.version : null;
|
|
2588
|
-
if (!latest) return { ok: false, message: "registry answered without a version" };
|
|
2589
|
-
return { ok: true, latest };
|
|
2590
|
-
} catch (error) {
|
|
2591
|
-
return { ok: false, message: error?.name === "AbortError" ? "timed out" : String(error?.message || error) };
|
|
2592
|
-
} finally {
|
|
2593
|
-
clearTimeout(timer);
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
async function checkForUpdate2({
|
|
2597
|
-
timeoutMs = 6e3,
|
|
2598
|
-
fetchImpl = globalThis.fetch,
|
|
2599
|
-
registries = registryCandidates()
|
|
2600
|
-
} = {}) {
|
|
2601
|
-
const current = currentVersion();
|
|
2602
|
-
const failures = [];
|
|
2603
|
-
for (const registry of registries) {
|
|
2604
|
-
const attempt = await askRegistry(registry, { timeoutMs, fetchImpl });
|
|
2605
|
-
if (attempt.ok) {
|
|
2606
|
-
return {
|
|
2607
|
-
ok: true,
|
|
2608
|
-
current,
|
|
2609
|
-
latest: attempt.latest,
|
|
2610
|
-
registry,
|
|
2611
|
-
updateAvailable: compareVersions(attempt.latest, current) > 0
|
|
2612
|
-
};
|
|
2613
|
-
}
|
|
2614
|
-
failures.push(`${registry}: ${attempt.message}`);
|
|
2615
|
-
}
|
|
2616
|
-
return {
|
|
2617
|
-
ok: false,
|
|
2618
|
-
current,
|
|
2619
|
-
errorKey: "update.errorNetwork",
|
|
2620
|
-
message: failures.join("; "),
|
|
2621
|
-
triedRegistries: registries
|
|
2622
|
-
};
|
|
2623
|
-
}
|
|
2624
|
-
function performUpdate2({
|
|
2625
|
-
spawnImpl = spawn,
|
|
2626
|
-
timeoutMs = 18e4,
|
|
2627
|
-
// Seam for tests: the suite runs from a checkout, where the guard below is
|
|
2628
|
-
// correctly the only reachable outcome.
|
|
2629
|
-
installKindImpl = installKind2,
|
|
2630
|
-
// The registry that just answered the version check. Installing from
|
|
2631
|
-
// somewhere the machine has been shown to reach beats installing from a
|
|
2632
|
-
// default it may have no route to. Empty means "whatever npm is configured
|
|
2633
|
-
// with", which is the right answer when no check has run.
|
|
2634
|
-
registry = ""
|
|
2635
|
-
} = {}) {
|
|
2636
|
-
return new Promise((resolve) => {
|
|
2637
|
-
const kind = installKindImpl();
|
|
2638
|
-
if (kind !== "npm") {
|
|
2639
|
-
resolve({ ok: false, errorKey: `update.cannot.${kind}` });
|
|
2640
|
-
return;
|
|
2641
|
-
}
|
|
2642
|
-
const args = ["install", "-g", `${PACKAGE_NAME}@latest`];
|
|
2643
|
-
const normalizedRegistry = normalizeRegistry(registry);
|
|
2644
|
-
if (normalizedRegistry) args.push("--registry", normalizedRegistry);
|
|
2645
|
-
const child = spawnImpl("npm", args, {
|
|
2646
|
-
encoding: "utf8",
|
|
2647
|
-
timeout: timeoutMs
|
|
2648
|
-
});
|
|
2649
|
-
let output = "";
|
|
2650
|
-
const collect = (chunk) => {
|
|
2651
|
-
output += String(chunk);
|
|
2652
|
-
};
|
|
2653
|
-
child.stdout?.on("data", collect);
|
|
2654
|
-
child.stderr?.on("data", collect);
|
|
2655
|
-
child.on("error", (error) => {
|
|
2656
|
-
resolve({ ok: false, errorKey: "update.errorFailed", output: error.message });
|
|
2657
|
-
});
|
|
2658
|
-
child.on("close", (code) => {
|
|
2659
|
-
resolve({ ok: code === 0, output: output.trim().slice(-2e3) });
|
|
2660
|
-
});
|
|
2661
|
-
});
|
|
2662
|
-
}
|
|
2663
|
-
module.exports = {
|
|
2664
|
-
PACKAGE_NAME,
|
|
2665
|
-
DEFAULT_REGISTRY,
|
|
2666
|
-
MIRROR_REGISTRY,
|
|
2667
|
-
canSelfUpdate: canSelfUpdate2,
|
|
2668
|
-
checkForUpdate: checkForUpdate2,
|
|
2669
|
-
compareVersions,
|
|
2670
|
-
currentVersion,
|
|
2671
|
-
installKind: installKind2,
|
|
2672
|
-
performUpdate: performUpdate2,
|
|
2673
|
-
registryCandidates
|
|
2674
|
-
};
|
|
2675
|
-
}
|
|
2676
|
-
});
|
|
2677
|
-
|
|
2678
2721
|
// tui/src/index.tsx
|
|
2679
2722
|
import { render } from "ink";
|
|
2680
2723
|
|
|
@@ -2976,6 +3019,7 @@ var STATUS_GLYPH = {
|
|
|
2976
3019
|
|
|
2977
3020
|
// tui/src/api.ts
|
|
2978
3021
|
var import_config = __toESM(require_config());
|
|
3022
|
+
var import_herdr_command = __toESM(require_herdr_command());
|
|
2979
3023
|
var import_i18n = __toESM(require_i18n());
|
|
2980
3024
|
var import_net_interfaces = __toESM(require_net_interfaces());
|
|
2981
3025
|
var import_settings_model = __toESM(require_settings_model());
|
|
@@ -3879,6 +3923,11 @@ function HerdrScreen({ ctx }) {
|
|
|
3879
3923
|
] })
|
|
3880
3924
|
] }) }),
|
|
3881
3925
|
registration?.packageRoot ? /* @__PURE__ */ jsx9(Row, { label: "", children: /* @__PURE__ */ jsx9(Text8, { color: theme.muted, children: registration.packageRoot }) }) : null,
|
|
3926
|
+
registration?.version ? /* @__PURE__ */ jsx9(Row, { label: t("herdr.version"), children: /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
3927
|
+
/* @__PURE__ */ jsx9(StatusDot, { level: registration.versionSupported === false ? "warn" : "ok" }),
|
|
3928
|
+
/* @__PURE__ */ jsx9(Text8, { children: ` ${registration.version}` })
|
|
3929
|
+
] }) }) : null,
|
|
3930
|
+
registration?.versionSupported === false ? /* @__PURE__ */ jsx9(Text8, { color: theme.warn, children: t("herdr.versionOutdated", { version: registration.version ?? "", minimum: import_herdr_command.MIN_HERDR_VERSION }) }) : null,
|
|
3882
3931
|
registration && !registration.available ? /* @__PURE__ */ jsx9(Text8, { color: theme.warn, children: t("herdr.cliMissing") }) : null,
|
|
3883
3932
|
registration?.stale && registration.linkedPath ? (
|
|
3884
3933
|
// A leftover link to an old source checkout is the usual state after
|
|
@@ -4015,7 +4064,7 @@ function About({ ctx }) {
|
|
|
4015
4064
|
children: updateLabel
|
|
4016
4065
|
}
|
|
4017
4066
|
) }),
|
|
4018
|
-
/* @__PURE__ */ jsx10(Row, { label: t("about.version"), children: /* @__PURE__ */ jsx10(Text9, { color: theme.muted, children: "0.2.
|
|
4067
|
+
/* @__PURE__ */ jsx10(Row, { label: t("about.version"), children: /* @__PURE__ */ jsx10(Text9, { color: theme.muted, children: "0.2.13" }) }),
|
|
4019
4068
|
/* @__PURE__ */ jsx10(Row, { label: t("about.configPath"), children: /* @__PURE__ */ jsx10(Text9, { color: theme.muted, children: (0, import_config.configPath)() }) }),
|
|
4020
4069
|
/* @__PURE__ */ jsx10(Row, { label: t("about.statePath"), children: /* @__PURE__ */ jsx10(Text9, { color: theme.muted, children: (0, import_config.stateDir)() }) }),
|
|
4021
4070
|
/* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text9, { color: theme.muted, children: t("about.docs") }) }),
|