@hermespilot/link 0.3.8 → 0.3.9
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/{chunk-RJB5VUT4.js → chunk-7OVDWXR7.js} +253 -76
- package/dist/cli/index.js +89 -33
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -3970,7 +3970,7 @@ import os2 from "os";
|
|
|
3970
3970
|
import path5 from "path";
|
|
3971
3971
|
|
|
3972
3972
|
// src/constants.ts
|
|
3973
|
-
var LINK_VERSION = "0.3.
|
|
3973
|
+
var LINK_VERSION = "0.3.9";
|
|
3974
3974
|
var LINK_COMMAND = "hermeslink";
|
|
3975
3975
|
var LINK_DEFAULT_PORT = 52379;
|
|
3976
3976
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -4455,6 +4455,9 @@ var DEFAULT_START_TIMEOUT_MS = 12e3;
|
|
|
4455
4455
|
var HEALTH_TIMEOUT_MS = 1500;
|
|
4456
4456
|
var MIN_API_SERVER_VERSION = "0.4.0";
|
|
4457
4457
|
var PROFILE_NAME_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
4458
|
+
var DASHBOARD_STATUS_URL = "http://127.0.0.1:9119/api/status";
|
|
4459
|
+
var DASHBOARD_STATUS_TIMEOUT_MS = 1500;
|
|
4460
|
+
var MAX_VERSION_LOG_OUTPUT_LENGTH = 1200;
|
|
4458
4461
|
var gatewayStartInFlightByProfile = /* @__PURE__ */ new Map();
|
|
4459
4462
|
async function ensureHermesApiServerAvailable(options = {}) {
|
|
4460
4463
|
const profileName = normalizeProfileName(options.profileName);
|
|
@@ -4571,28 +4574,56 @@ async function reloadHermesGateway(options = {}) {
|
|
|
4571
4574
|
}
|
|
4572
4575
|
return ensureHermesApiServerAvailable({ ...options, forceRestart: true });
|
|
4573
4576
|
}
|
|
4574
|
-
async function readHermesVersion() {
|
|
4575
|
-
const { stdout } = await execHermesVersion();
|
|
4576
|
-
const raw = stdout.trim();
|
|
4577
|
-
const version = parseHermesVersion(raw);
|
|
4578
|
-
return {
|
|
4579
|
-
raw,
|
|
4580
|
-
version,
|
|
4581
|
-
supportsApiServer: version ? compareSemver(version, MIN_API_SERVER_VERSION) >= 0 : null
|
|
4582
|
-
};
|
|
4583
|
-
}
|
|
4584
|
-
async function execHermesVersion() {
|
|
4577
|
+
async function readHermesVersion(options = {}) {
|
|
4585
4578
|
try {
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
} catch {
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4579
|
+
const { stdout } = await execHermesVersion(options.logger);
|
|
4580
|
+
const raw = stdout.trim();
|
|
4581
|
+
const version = parseHermesVersion(raw);
|
|
4582
|
+
return buildHermesVersionInfo(raw, version);
|
|
4583
|
+
} catch (cliError) {
|
|
4584
|
+
const dashboardStatusUrl = options.dashboardStatusUrl ?? DASHBOARD_STATUS_URL;
|
|
4585
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_requested", {
|
|
4586
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4587
|
+
reason: cliError instanceof Error ? cliError.message : String(cliError)
|
|
4594
4588
|
});
|
|
4589
|
+
try {
|
|
4590
|
+
const fallback = await readHermesDashboardVersion({
|
|
4591
|
+
fetchImpl: options.fetchImpl,
|
|
4592
|
+
statusUrl: dashboardStatusUrl,
|
|
4593
|
+
timeoutMs: options.dashboardTimeoutMs
|
|
4594
|
+
});
|
|
4595
|
+
void options.logger?.info("hermes_version_dashboard_fallback_succeeded", {
|
|
4596
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4597
|
+
hermes_version: fallback.version
|
|
4598
|
+
});
|
|
4599
|
+
return fallback;
|
|
4600
|
+
} catch (dashboardError) {
|
|
4601
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_failed", {
|
|
4602
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
4603
|
+
error: dashboardError instanceof Error ? dashboardError.message : String(dashboardError)
|
|
4604
|
+
});
|
|
4605
|
+
throw new Error(
|
|
4606
|
+
`Hermes version detection failed. CLI: ${cliError instanceof Error ? cliError.message : String(cliError)}; dashboard fallback: ${dashboardError instanceof Error ? dashboardError.message : String(dashboardError)}`
|
|
4607
|
+
);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
async function execHermesVersion(logger) {
|
|
4612
|
+
const hermesBin = resolveHermesBin();
|
|
4613
|
+
const failures = [];
|
|
4614
|
+
for (const args of [["version"], ["--version"]]) {
|
|
4615
|
+
try {
|
|
4616
|
+
return await execFileAsync2(hermesBin, args, {
|
|
4617
|
+
timeout: 5e3,
|
|
4618
|
+
windowsHide: true
|
|
4619
|
+
});
|
|
4620
|
+
} catch (error) {
|
|
4621
|
+
const failure = describeVersionCommandFailure(hermesBin, args, error);
|
|
4622
|
+
failures.push(failure.summary);
|
|
4623
|
+
void logger?.warn("hermes_version_cli_command_failed", failure.fields);
|
|
4624
|
+
}
|
|
4595
4625
|
}
|
|
4626
|
+
throw new Error(failures.join("; "));
|
|
4596
4627
|
}
|
|
4597
4628
|
function assertHermesRunsApiSupported(version, status) {
|
|
4598
4629
|
if (status !== 404) {
|
|
@@ -4617,7 +4648,7 @@ async function startHermesGatewayOnce(paths, profileName, logger) {
|
|
|
4617
4648
|
return await gatewayStartInFlightByProfile.get(profileName);
|
|
4618
4649
|
}
|
|
4619
4650
|
async function startHermesGateway(paths, profileName, logger) {
|
|
4620
|
-
const version = await readHermesVersion().catch((error) => {
|
|
4651
|
+
const version = await readHermesVersion({ logger }).catch((error) => {
|
|
4621
4652
|
void logger?.error("gateway_hermes_cli_unavailable", {
|
|
4622
4653
|
error: error instanceof Error ? error.message : String(error)
|
|
4623
4654
|
});
|
|
@@ -4729,7 +4760,7 @@ async function restartHermesGatewayServiceIfAvailable(options) {
|
|
|
4729
4760
|
return {
|
|
4730
4761
|
pid: null,
|
|
4731
4762
|
logPath,
|
|
4732
|
-
version: await readHermesVersion().catch(() => null),
|
|
4763
|
+
version: await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
4733
4764
|
...logHint ? { logHint } : {}
|
|
4734
4765
|
};
|
|
4735
4766
|
}
|
|
@@ -4972,6 +5003,72 @@ function parseHermesVersion(value) {
|
|
|
4972
5003
|
const match = /\bv?(\d+\.\d+\.\d+)\b/u.exec(value);
|
|
4973
5004
|
return match?.[1] ?? null;
|
|
4974
5005
|
}
|
|
5006
|
+
function buildHermesVersionInfo(raw, version) {
|
|
5007
|
+
return {
|
|
5008
|
+
raw,
|
|
5009
|
+
version,
|
|
5010
|
+
supportsApiServer: version ? compareSemver(version, MIN_API_SERVER_VERSION) >= 0 : null
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
async function readHermesDashboardVersion(options = {}) {
|
|
5014
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
5015
|
+
const controller = new AbortController();
|
|
5016
|
+
const timer = setTimeout(
|
|
5017
|
+
() => controller.abort(),
|
|
5018
|
+
options.timeoutMs ?? DASHBOARD_STATUS_TIMEOUT_MS
|
|
5019
|
+
);
|
|
5020
|
+
try {
|
|
5021
|
+
const response = await fetcher(options.statusUrl ?? DASHBOARD_STATUS_URL, {
|
|
5022
|
+
method: "GET",
|
|
5023
|
+
headers: { accept: "application/json" },
|
|
5024
|
+
signal: controller.signal
|
|
5025
|
+
});
|
|
5026
|
+
if (!response.ok) {
|
|
5027
|
+
throw new Error(`Hermes dashboard returned HTTP ${response.status}`);
|
|
5028
|
+
}
|
|
5029
|
+
const payload = await response.json().catch(() => null);
|
|
5030
|
+
const record = toRecord2(payload);
|
|
5031
|
+
const versionText = readString4(record, "version");
|
|
5032
|
+
if (!versionText) {
|
|
5033
|
+
throw new Error("Hermes dashboard status did not include a version");
|
|
5034
|
+
}
|
|
5035
|
+
const raw = truncateVersionLogOutput(JSON.stringify(record));
|
|
5036
|
+
const version = parseHermesVersion(versionText) ?? parseHermesVersion(raw);
|
|
5037
|
+
return buildHermesVersionInfo(raw, version);
|
|
5038
|
+
} catch (error) {
|
|
5039
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
5040
|
+
throw new Error("Hermes dashboard version probe timed out");
|
|
5041
|
+
}
|
|
5042
|
+
throw error;
|
|
5043
|
+
} finally {
|
|
5044
|
+
clearTimeout(timer);
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
function describeVersionCommandFailure(hermesBin, args, error) {
|
|
5048
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5049
|
+
const output = truncateVersionLogOutput(readExecErrorOutput2(error));
|
|
5050
|
+
return {
|
|
5051
|
+
summary: `${hermesBin} ${args.join(" ")} failed: ${message}`,
|
|
5052
|
+
fields: {
|
|
5053
|
+
hermes_bin: hermesBin,
|
|
5054
|
+
command: args.join(" "),
|
|
5055
|
+
error: message,
|
|
5056
|
+
...output ? { output } : {}
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
function readExecErrorOutput2(error) {
|
|
5061
|
+
if (typeof error !== "object" || error === null) {
|
|
5062
|
+
return "";
|
|
5063
|
+
}
|
|
5064
|
+
const stdout = "stdout" in error && error.stdout != null ? String(error.stdout) : "";
|
|
5065
|
+
const stderr = "stderr" in error && error.stderr != null ? String(error.stderr) : "";
|
|
5066
|
+
return `${stdout}
|
|
5067
|
+
${stderr}`.trim();
|
|
5068
|
+
}
|
|
5069
|
+
function truncateVersionLogOutput(value) {
|
|
5070
|
+
return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
|
|
5071
|
+
}
|
|
4975
5072
|
function compareSemver(left, right) {
|
|
4976
5073
|
const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
|
|
4977
5074
|
const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
|
|
@@ -10671,7 +10768,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
10671
10768
|
);
|
|
10672
10769
|
if (response.status === 404 || response.status === 503) {
|
|
10673
10770
|
assertHermesRunsApiSupported(
|
|
10674
|
-
await readHermesVersion().catch(() => null),
|
|
10771
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10675
10772
|
response.status
|
|
10676
10773
|
);
|
|
10677
10774
|
throw new LinkHttpError(
|
|
@@ -10698,7 +10795,7 @@ async function streamHermesRunEvents(runId, options = {}) {
|
|
|
10698
10795
|
options
|
|
10699
10796
|
);
|
|
10700
10797
|
assertHermesRunsApiSupported(
|
|
10701
|
-
await readHermesVersion().catch(() => null),
|
|
10798
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10702
10799
|
response.status
|
|
10703
10800
|
);
|
|
10704
10801
|
if (!response.ok || !response.body) {
|
|
@@ -10741,7 +10838,7 @@ async function streamHermesResponses(input, options = {}) {
|
|
|
10741
10838
|
);
|
|
10742
10839
|
if (response.status === 404 || response.status === 503) {
|
|
10743
10840
|
assertHermesRunsApiSupported(
|
|
10744
|
-
await readHermesVersion().catch(() => null),
|
|
10841
|
+
await readHermesVersion({ logger: options.logger }).catch(() => null),
|
|
10745
10842
|
response.status
|
|
10746
10843
|
);
|
|
10747
10844
|
throw new LinkHttpError(
|
|
@@ -19376,7 +19473,10 @@ var runningUpdate = null;
|
|
|
19376
19473
|
async function readHermesUpdateCheck(options) {
|
|
19377
19474
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
19378
19475
|
const [local, remoteResult] = await Promise.all([
|
|
19379
|
-
readHermesVersion(
|
|
19476
|
+
readHermesVersion({
|
|
19477
|
+
fetchImpl: options.fetchImpl,
|
|
19478
|
+
logger: options.logger
|
|
19479
|
+
}).catch((error) => ({
|
|
19380
19480
|
raw: error instanceof Error ? error.message : String(error),
|
|
19381
19481
|
version: null
|
|
19382
19482
|
})),
|
|
@@ -21437,6 +21537,17 @@ import path24 from "path";
|
|
|
21437
21537
|
import { rm as rm12 } from "fs/promises";
|
|
21438
21538
|
|
|
21439
21539
|
// src/relay/bootstrap.ts
|
|
21540
|
+
var RelayNetworkError = class extends Error {
|
|
21541
|
+
constructor(relayBaseUrl, causeMessage) {
|
|
21542
|
+
super(
|
|
21543
|
+
`Cannot reach Hermes Relay at ${relayBaseUrl}. Please check your network connection, VPN, or proxy settings, then try again.`
|
|
21544
|
+
);
|
|
21545
|
+
this.relayBaseUrl = relayBaseUrl;
|
|
21546
|
+
this.causeMessage = causeMessage;
|
|
21547
|
+
}
|
|
21548
|
+
relayBaseUrl;
|
|
21549
|
+
causeMessage;
|
|
21550
|
+
};
|
|
21440
21551
|
async function bootstrapRelayLink(options) {
|
|
21441
21552
|
const fetcher = options.fetchImpl ?? fetch;
|
|
21442
21553
|
const baseUrl = options.relayBaseUrl.replace(/\/+$/u, "");
|
|
@@ -21477,14 +21588,23 @@ async function bootstrapRelayLink(options) {
|
|
|
21477
21588
|
};
|
|
21478
21589
|
}
|
|
21479
21590
|
async function postJson(fetcher, url, token, body) {
|
|
21480
|
-
|
|
21481
|
-
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
|
|
21485
|
-
|
|
21486
|
-
|
|
21487
|
-
|
|
21591
|
+
let response;
|
|
21592
|
+
try {
|
|
21593
|
+
response = await fetcher(url, {
|
|
21594
|
+
method: "POST",
|
|
21595
|
+
headers: {
|
|
21596
|
+
authorization: `Bearer ${token}`,
|
|
21597
|
+
"content-type": "application/json"
|
|
21598
|
+
},
|
|
21599
|
+
body: JSON.stringify(body)
|
|
21600
|
+
});
|
|
21601
|
+
} catch (error) {
|
|
21602
|
+
const baseUrl = new URL(url).origin;
|
|
21603
|
+
throw new RelayNetworkError(
|
|
21604
|
+
baseUrl,
|
|
21605
|
+
error instanceof Error ? error.message : String(error)
|
|
21606
|
+
);
|
|
21607
|
+
}
|
|
21488
21608
|
const payload = await response.json().catch(() => null);
|
|
21489
21609
|
if (!response.ok) {
|
|
21490
21610
|
const message = readErrorMessage4(payload) ?? `Relay request failed with HTTP ${response.status}`;
|
|
@@ -21512,14 +21632,22 @@ async function preparePairing(paths = resolveRuntimePaths()) {
|
|
|
21512
21632
|
const config = await loadConfig(paths);
|
|
21513
21633
|
const identity = await ensureIdentity(paths);
|
|
21514
21634
|
const systemInfo = readLinkSystemInfo();
|
|
21515
|
-
const created = await postServerJson(
|
|
21516
|
-
|
|
21517
|
-
|
|
21518
|
-
|
|
21519
|
-
|
|
21520
|
-
|
|
21521
|
-
|
|
21522
|
-
|
|
21635
|
+
const created = await postServerJson(
|
|
21636
|
+
config.serverBaseUrl,
|
|
21637
|
+
"/api/v1/link-pairings",
|
|
21638
|
+
{
|
|
21639
|
+
install_id: identity.install_id,
|
|
21640
|
+
link_id: identity.link_id ?? void 0,
|
|
21641
|
+
display_name: systemInfo.defaultDisplayName,
|
|
21642
|
+
platform: systemInfo.platform,
|
|
21643
|
+
hostname: systemInfo.hostname ?? void 0,
|
|
21644
|
+
public_key_pem: identity.public_key_pem
|
|
21645
|
+
},
|
|
21646
|
+
{
|
|
21647
|
+
target: "server",
|
|
21648
|
+
action: "create pairing session"
|
|
21649
|
+
}
|
|
21650
|
+
);
|
|
21523
21651
|
const relayBaseUrl = created.relayBaseUrl || config.relayBaseUrl;
|
|
21524
21652
|
let assigned;
|
|
21525
21653
|
let updatedIdentity;
|
|
@@ -21541,28 +21669,43 @@ async function preparePairing(paths = resolveRuntimePaths()) {
|
|
|
21541
21669
|
installId: updatedIdentity.install_id,
|
|
21542
21670
|
publicKeyPem: updatedIdentity.public_key_pem
|
|
21543
21671
|
});
|
|
21544
|
-
await patchServerJson(
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21672
|
+
await patchServerJson(
|
|
21673
|
+
config.serverBaseUrl,
|
|
21674
|
+
`/api/v1/link-pairings/${created.sessionId}/link`,
|
|
21675
|
+
created.pairingToken,
|
|
21676
|
+
{
|
|
21677
|
+
install_id: updatedIdentity.install_id,
|
|
21678
|
+
link_id: assigned.linkId,
|
|
21679
|
+
link_version: LINK_VERSION,
|
|
21680
|
+
display_name: systemInfo.defaultDisplayName,
|
|
21681
|
+
platform: systemInfo.platform,
|
|
21682
|
+
hostname: systemInfo.hostname ?? void 0,
|
|
21683
|
+
lan_ips: routes.lanIps,
|
|
21684
|
+
public_ipv4s: routes.publicIpv4s,
|
|
21685
|
+
public_ipv6s: routes.publicIpv6s,
|
|
21686
|
+
preferred_urls: routes.preferredUrls,
|
|
21687
|
+
environment: routes.environment
|
|
21688
|
+
},
|
|
21689
|
+
{
|
|
21690
|
+
target: "server",
|
|
21691
|
+
action: "finalize pairing"
|
|
21692
|
+
}
|
|
21693
|
+
);
|
|
21557
21694
|
} catch (error) {
|
|
21695
|
+
const reportedError = error instanceof RelayNetworkError ? createPairingNetworkError({
|
|
21696
|
+
target: "relay",
|
|
21697
|
+
action: "connect to Relay",
|
|
21698
|
+
baseUrl: error.relayBaseUrl,
|
|
21699
|
+
detail: error.causeMessage
|
|
21700
|
+
}) : error;
|
|
21558
21701
|
await reportPairingErrorToServer({
|
|
21559
21702
|
serverBaseUrl: config.serverBaseUrl,
|
|
21560
21703
|
sessionId: created.sessionId,
|
|
21561
21704
|
source: "link",
|
|
21562
21705
|
pairingToken: created.pairingToken,
|
|
21563
|
-
error: pairingErrorSnapshot("prepare_pairing",
|
|
21706
|
+
error: pairingErrorSnapshot("prepare_pairing", reportedError)
|
|
21564
21707
|
});
|
|
21565
|
-
throw
|
|
21708
|
+
throw reportedError;
|
|
21566
21709
|
}
|
|
21567
21710
|
const qrPayload = {
|
|
21568
21711
|
kind: "hermes_link_pairing",
|
|
@@ -21662,6 +21805,10 @@ async function claimPairing(input) {
|
|
|
21662
21805
|
{
|
|
21663
21806
|
claim_token: input.claimToken,
|
|
21664
21807
|
app_instance_id: input.appInstanceId ?? void 0
|
|
21808
|
+
},
|
|
21809
|
+
{
|
|
21810
|
+
target: "server",
|
|
21811
|
+
action: "verify pairing claim"
|
|
21665
21812
|
}
|
|
21666
21813
|
);
|
|
21667
21814
|
} catch (error) {
|
|
@@ -21709,15 +21856,25 @@ async function loadRequiredIdentity2(paths) {
|
|
|
21709
21856
|
}
|
|
21710
21857
|
return identity;
|
|
21711
21858
|
}
|
|
21712
|
-
async function postServerJson(serverBaseUrl, path25, body) {
|
|
21713
|
-
|
|
21714
|
-
|
|
21715
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
21718
|
-
|
|
21719
|
-
|
|
21720
|
-
|
|
21859
|
+
async function postServerJson(serverBaseUrl, path25, body, options) {
|
|
21860
|
+
let response;
|
|
21861
|
+
try {
|
|
21862
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
21863
|
+
method: "POST",
|
|
21864
|
+
headers: {
|
|
21865
|
+
accept: "application/json",
|
|
21866
|
+
"content-type": "application/json"
|
|
21867
|
+
},
|
|
21868
|
+
body: JSON.stringify(body)
|
|
21869
|
+
});
|
|
21870
|
+
} catch (error) {
|
|
21871
|
+
throw createPairingNetworkError({
|
|
21872
|
+
target: options.target,
|
|
21873
|
+
action: options.action,
|
|
21874
|
+
baseUrl: serverBaseUrl,
|
|
21875
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
21876
|
+
});
|
|
21877
|
+
}
|
|
21721
21878
|
return readJsonResponse2(response);
|
|
21722
21879
|
}
|
|
21723
21880
|
async function reportPairingErrorToServer(input) {
|
|
@@ -21750,16 +21907,26 @@ function pairingErrorSnapshot(stage, error) {
|
|
|
21750
21907
|
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
21751
21908
|
};
|
|
21752
21909
|
}
|
|
21753
|
-
async function patchServerJson(serverBaseUrl, path25, token, body) {
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
21760
|
-
|
|
21761
|
-
|
|
21762
|
-
|
|
21910
|
+
async function patchServerJson(serverBaseUrl, path25, token, body, options) {
|
|
21911
|
+
let response;
|
|
21912
|
+
try {
|
|
21913
|
+
response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
|
|
21914
|
+
method: "PATCH",
|
|
21915
|
+
headers: {
|
|
21916
|
+
accept: "application/json",
|
|
21917
|
+
authorization: `Bearer ${token}`,
|
|
21918
|
+
"content-type": "application/json"
|
|
21919
|
+
},
|
|
21920
|
+
body: JSON.stringify(body)
|
|
21921
|
+
});
|
|
21922
|
+
} catch (error) {
|
|
21923
|
+
throw createPairingNetworkError({
|
|
21924
|
+
target: options.target,
|
|
21925
|
+
action: options.action,
|
|
21926
|
+
baseUrl: serverBaseUrl,
|
|
21927
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
21928
|
+
});
|
|
21929
|
+
}
|
|
21763
21930
|
return readJsonResponse2(response);
|
|
21764
21931
|
}
|
|
21765
21932
|
async function readJsonResponse2(response) {
|
|
@@ -21781,6 +21948,15 @@ function readErrorMessage5(payload) {
|
|
|
21781
21948
|
const message = error.message;
|
|
21782
21949
|
return typeof message === "string" ? message : null;
|
|
21783
21950
|
}
|
|
21951
|
+
function createPairingNetworkError(input) {
|
|
21952
|
+
const baseMessage = input.target === "server" ? `HermesPilot Server is unreachable while trying to ${input.action}.` : `Hermes Relay is unreachable while trying to ${input.action}.`;
|
|
21953
|
+
const hint = "If you are using a VPN, proxy, or corporate network, try turning it off and retrying.";
|
|
21954
|
+
return new LinkHttpError(
|
|
21955
|
+
503,
|
|
21956
|
+
input.target === "server" ? "pairing_server_unreachable" : "pairing_relay_unreachable",
|
|
21957
|
+
`${baseMessage} Please check whether ${input.baseUrl} is reachable. ${hint} Detail: ${input.detail}`
|
|
21958
|
+
);
|
|
21959
|
+
}
|
|
21784
21960
|
function pairingClaimPath(sessionId, paths) {
|
|
21785
21961
|
return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
|
|
21786
21962
|
}
|
|
@@ -22916,6 +23092,7 @@ export {
|
|
|
22916
23092
|
createFileLogger,
|
|
22917
23093
|
getLinkLogFile,
|
|
22918
23094
|
ensureHermesApiServerAvailable,
|
|
23095
|
+
readHermesVersion,
|
|
22919
23096
|
loadConfig,
|
|
22920
23097
|
saveConfig,
|
|
22921
23098
|
normalizeLanHost,
|
package/dist/cli/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
preparePairing,
|
|
23
23
|
probeLocalLinkService,
|
|
24
24
|
readHermesApiServerConfig,
|
|
25
|
+
readHermesVersion,
|
|
25
26
|
readPairingClaim,
|
|
26
27
|
reportLinkStatusToServer,
|
|
27
28
|
resolveHermesConfigPath,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
startDaemonProcess,
|
|
33
34
|
startLinkService,
|
|
34
35
|
stopDaemonProcess
|
|
35
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-7OVDWXR7.js";
|
|
36
37
|
|
|
37
38
|
// src/cli/index.ts
|
|
38
39
|
import { Command } from "commander";
|
|
@@ -273,16 +274,16 @@ var messages = {
|
|
|
273
274
|
"autostart.alreadyEnabled": "Boot autostart is already enabled via {method}: {path}",
|
|
274
275
|
"pair.description": "Create a Hermes Link pairing session",
|
|
275
276
|
"pair.preflight": "Checking local Hermes configuration before pairing...",
|
|
277
|
+
"pair.preflight.hermesFiles": "Checking Hermes data directory, config, and environment files...",
|
|
278
|
+
"pair.preflight.hermesCli": "Checking whether the Hermes CLI is available...",
|
|
279
|
+
"pair.preflight.hermesApiServer": "Checking whether the Hermes API Server is ready...",
|
|
276
280
|
"pair.hermesHome": "Hermes home: {path}",
|
|
281
|
+
"pair.hermesVersion": "Hermes CLI: {value}",
|
|
277
282
|
"pair.apiReady": "Hermes API Server is ready on 127.0.0.1:{port}",
|
|
278
|
-
"pair.preparing": "
|
|
279
|
-
"pair.
|
|
280
|
-
"pair.
|
|
281
|
-
"pair.
|
|
282
|
-
"pair.code": "Pairing code: {value}",
|
|
283
|
-
"pair.localApi": "Local API: http://127.0.0.1:{port}",
|
|
284
|
-
"pair.scan": "Open this pairing page in the HermesPilot App or scan the QR code below:",
|
|
285
|
-
"pair.openBrowserFailed": "Could not open the system browser automatically. You can still scan the QR code below or open this URL manually: {url}",
|
|
283
|
+
"pair.preparing": "Creating the pairing session...",
|
|
284
|
+
"pair.scan": "Please scan the QR code below in the HermesPilot App:",
|
|
285
|
+
"pair.openPairingPage": "If the QR code is hard to scan, you can open this page locally: {url}",
|
|
286
|
+
"pair.manualCode": "You can also use the HermesPilot App manual connection mode and enter this pairing code:",
|
|
286
287
|
"pair.expires": "Pairing expires in 10 minutes. Press Ctrl+C to cancel waiting.",
|
|
287
288
|
"pair.claimed": "Pairing succeeded. Starting Hermes Link in the background...",
|
|
288
289
|
"pair.claimedRunning": "Pairing succeeded. Hermes Link is already running in the background.",
|
|
@@ -298,6 +299,8 @@ var messages = {
|
|
|
298
299
|
"doctor.notAssigned": "not assigned",
|
|
299
300
|
"doctor.lanHost": "Configured LAN host: {value}",
|
|
300
301
|
"doctor.networkWarning": "Network note: {message}",
|
|
302
|
+
"doctor.hermesCli": "Hermes CLI: {value}",
|
|
303
|
+
"doctor.hermesCliUnavailable": "Hermes CLI is unavailable. Please make sure the `hermes` command can run in this system.",
|
|
301
304
|
"doctor.apiReady": "Hermes API Server: ready",
|
|
302
305
|
"doctor.apiStarted": "Hermes API Server: started and ready",
|
|
303
306
|
"doctor.apiUnavailable": "Hermes API Server: unavailable. {message}",
|
|
@@ -306,6 +309,8 @@ var messages = {
|
|
|
306
309
|
"error.relayLinkInvalid": "Relay did not return a valid link_id.",
|
|
307
310
|
"error.relayEmpty": "Relay returned an empty response.",
|
|
308
311
|
"error.serverHttp": "HermesPilot Server request failed with HTTP {status}.",
|
|
312
|
+
"error.pairingServerUnreachable": "Could not reach HermesPilot Server while creating the pairing session. Check whether {url} is reachable, then try again. If you use a proxy network, add hermes-server.clawpilot.me and hermes-relay.clawpilot.me to the proxy exclusion list, or temporarily turn off VPN/proxy and retry.",
|
|
313
|
+
"error.pairingRelayUnreachable": "Could not reach Hermes Relay while creating the pairing session. Check whether {url} is reachable, then try again. If you use a proxy network, add hermes-server.clawpilot.me and hermes-relay.clawpilot.me to the proxy exclusion list, or temporarily turn off VPN/proxy and retry.",
|
|
309
314
|
"error.portInUse": "Local port {port} is already in use by another process. Stop that process or change the Hermes Link port, then run `hermeslink pair` again.",
|
|
310
315
|
"error.pairingRequires": "Pairing needs HermesPilot Server and Relay, but this command could not start a complete pairing session.",
|
|
311
316
|
"error.pairingRequires.detail": "The deployed services may be healthy, but the installed Link package must call Server for a short-lived relay bootstrap token before it can request a link_id."
|
|
@@ -363,16 +368,16 @@ var messages = {
|
|
|
363
368
|
"autostart.alreadyEnabled": "\u5F00\u673A\u81EA\u542F\u5DF2\u542F\u7528\uFF0C\u65B9\u5F0F\uFF1A{method}\uFF0C\u6587\u4EF6\uFF1A{path}",
|
|
364
369
|
"pair.description": "\u521B\u5EFA Hermes Link \u914D\u5BF9\u4F1A\u8BDD",
|
|
365
370
|
"pair.preflight": "\u6B63\u5728\u914D\u5BF9\u524D\u68C0\u67E5\u672C\u673A Hermes \u914D\u7F6E...",
|
|
371
|
+
"pair.preflight.hermesFiles": "\u6B63\u5728\u68C0\u67E5 Hermes \u6570\u636E\u76EE\u5F55\u3001\u914D\u7F6E\u6587\u4EF6\u548C\u73AF\u5883\u6587\u4EF6...",
|
|
372
|
+
"pair.preflight.hermesCli": "\u6B63\u5728\u68C0\u67E5 Hermes CLI \u662F\u5426\u53EF\u7528...",
|
|
373
|
+
"pair.preflight.hermesApiServer": "\u6B63\u5728\u68C0\u67E5 Hermes API Server \u662F\u5426\u5C31\u7EEA...",
|
|
366
374
|
"pair.hermesHome": "Hermes \u6570\u636E\u76EE\u5F55\uFF1A{path}",
|
|
375
|
+
"pair.hermesVersion": "Hermes CLI\uFF1A{value}",
|
|
367
376
|
"pair.apiReady": "Hermes API Server \u5DF2\u5C31\u7EEA\uFF1A127.0.0.1:{port}",
|
|
368
|
-
"pair.preparing": "\u6B63\u5728\
|
|
369
|
-
"pair.
|
|
370
|
-
"pair.
|
|
371
|
-
"pair.
|
|
372
|
-
"pair.code": "\u914D\u5BF9\u7801\uFF1A{value}",
|
|
373
|
-
"pair.localApi": "\u672C\u5730 API\uFF1Ahttp://127.0.0.1:{port}",
|
|
374
|
-
"pair.scan": "\u8BF7\u5728 HermesPilot App \u4E2D\u6253\u5F00\u8FD9\u4E2A\u914D\u5BF9\u9875\uFF0C\u6216\u626B\u63CF\u4E0B\u9762\u7684\u4E8C\u7EF4\u7801\uFF1A",
|
|
375
|
-
"pair.openBrowserFailed": "\u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u7CFB\u7EDF\u6D4F\u89C8\u5668\u3002\u4F60\u4ECD\u7136\u53EF\u4EE5\u626B\u63CF\u4E0B\u9762\u7684\u4E8C\u7EF4\u7801\uFF0C\u6216\u624B\u52A8\u6253\u5F00\u8FD9\u4E2A\u94FE\u63A5\uFF1A{url}",
|
|
377
|
+
"pair.preparing": "\u6B63\u5728\u521B\u5EFA\u914D\u5BF9\u4F1A\u8BDD...",
|
|
378
|
+
"pair.scan": "\u8BF7\u5728 HermesPilot App \u4E2D\u626B\u7801\u4E0B\u9762\u7684\u4E8C\u7EF4\u7801\uFF1A",
|
|
379
|
+
"pair.openPairingPage": "\u5982\u679C\u4E8C\u7EF4\u7801\u4E0D\u5BB9\u6613\u626B\u63CF\uFF0C\u4F60\u53EF\u4EE5\u5728\u672C\u673A\u6253\u5F00\u8FD9\u4E2A\u9875\u9762\uFF1A{url}",
|
|
380
|
+
"pair.manualCode": "\u4F60\u4E5F\u53EF\u4EE5\u5728 HermesPilot App \u4E2D\u4F7F\u7528\u624B\u52A8\u8FDE\u63A5\u6A21\u5F0F\uFF0C\u8F93\u5165\u4EE5\u4E0B\u914D\u5BF9\u7801\u8FDB\u884C\u8FDE\u63A5\uFF1A",
|
|
376
381
|
"pair.expires": "\u914D\u5BF9\u4F1A\u8BDD 10 \u5206\u949F\u540E\u8FC7\u671F\u3002\u6309 Ctrl+C \u9000\u51FA\u7B49\u5F85\u3002",
|
|
377
382
|
"pair.claimed": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002\u6B63\u5728\u628A Hermes Link \u5207\u6362\u5230\u540E\u53F0\u8FD0\u884C...",
|
|
378
383
|
"pair.claimedRunning": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002Hermes Link \u5DF2\u5728\u540E\u53F0\u6301\u7EED\u8FD0\u884C\u3002",
|
|
@@ -388,6 +393,8 @@ var messages = {
|
|
|
388
393
|
"doctor.notAssigned": "\u5C1A\u672A\u5206\u914D",
|
|
389
394
|
"doctor.lanHost": "\u5DF2\u914D\u7F6E\u5C40\u57DF\u7F51\u4E3B\u673A\uFF1A{value}",
|
|
390
395
|
"doctor.networkWarning": "\u7F51\u7EDC\u63D0\u793A\uFF1A{message}",
|
|
396
|
+
"doctor.hermesCli": "Hermes CLI\uFF1A{value}",
|
|
397
|
+
"doctor.hermesCliUnavailable": "Hermes CLI\uFF1A\u4E0D\u53EF\u7528\u3002\u8BF7\u786E\u8BA4\u5F53\u524D\u7CFB\u7EDF\u53EF\u4EE5\u76F4\u63A5\u8FD0\u884C `hermes` \u547D\u4EE4\u3002",
|
|
391
398
|
"doctor.apiReady": "Hermes API Server\uFF1A\u5DF2\u5C31\u7EEA",
|
|
392
399
|
"doctor.apiStarted": "Hermes API Server\uFF1A\u5DF2\u81EA\u52A8\u542F\u52A8\u5E76\u5C31\u7EEA",
|
|
393
400
|
"doctor.apiUnavailable": "Hermes API Server\uFF1A\u4E0D\u53EF\u7528\u3002{message}",
|
|
@@ -396,6 +403,8 @@ var messages = {
|
|
|
396
403
|
"error.relayLinkInvalid": "Relay \u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684 link_id\u3002",
|
|
397
404
|
"error.relayEmpty": "Relay \u8FD4\u56DE\u4E86\u7A7A\u54CD\u5E94\u3002",
|
|
398
405
|
"error.serverHttp": "HermesPilot Server \u8BF7\u6C42\u5931\u8D25\uFF0CHTTP \u72B6\u6001\u7801\uFF1A{status}\u3002",
|
|
406
|
+
"error.pairingServerUnreachable": "\u521B\u5EFA\u914D\u5BF9\u4F1A\u8BDD\u65F6\u65E0\u6CD5\u8FDE\u63A5 HermesPilot Server\u3002\u8BF7\u5148\u786E\u8BA4 {url} \u53EF\u4EE5\u8BBF\u95EE\uFF0C\u7136\u540E\u91CD\u8BD5\u3002\u91CD\u70B9\u63D0\u9192\uFF1A\u5982\u679C\u4F60\u4F7F\u7528\u4E86\u4EE3\u7406\u7F51\u7EDC\uFF0C\u53EF\u4EE5\u628A hermes-server.clawpilot.me \u548C hermes-relay.clawpilot.me \u52A0\u5165\u4EE3\u7406\u6392\u9664\u540D\u5355\uFF0C\u6216\u4E34\u65F6\u5173\u95ED VPN/\u4EE3\u7406\u540E\u518D\u8BD5\u3002",
|
|
407
|
+
"error.pairingRelayUnreachable": "\u521B\u5EFA\u914D\u5BF9\u4F1A\u8BDD\u65F6\u65E0\u6CD5\u8FDE\u63A5 Hermes Relay\u3002\u8BF7\u5148\u786E\u8BA4 {url} \u53EF\u4EE5\u8BBF\u95EE\uFF0C\u7136\u540E\u91CD\u8BD5\u3002\u91CD\u70B9\u63D0\u9192\uFF1A\u5982\u679C\u4F60\u4F7F\u7528\u4E86\u4EE3\u7406\u7F51\u7EDC\uFF0C\u53EF\u4EE5\u628A hermes-server.clawpilot.me \u548C hermes-relay.clawpilot.me \u52A0\u5165\u4EE3\u7406\u6392\u9664\u540D\u5355\uFF0C\u6216\u4E34\u65F6\u5173\u95ED VPN/\u4EE3\u7406\u540E\u518D\u8BD5\u3002",
|
|
399
408
|
"error.portInUse": "\u672C\u5730\u7AEF\u53E3 {port} \u5DF2\u88AB\u5176\u4ED6\u8FDB\u7A0B\u5360\u7528\u3002\u8BF7\u5148\u505C\u6B62\u5360\u7528\u8BE5\u7AEF\u53E3\u7684\u7A0B\u5E8F\uFF0C\u6216\u8C03\u6574 Hermes Link \u7AEF\u53E3\u540E\u91CD\u65B0\u8FD0\u884C `hermeslink pair`\u3002",
|
|
400
409
|
"error.pairingRequires": "\u914D\u5BF9\u9700\u8981 HermesPilot Server \u548C Relay\uFF0C\u4F46\u5F53\u524D\u547D\u4EE4\u6CA1\u6709\u80FD\u542F\u52A8\u5B8C\u6574\u914D\u5BF9\u4F1A\u8BDD\u3002",
|
|
401
410
|
"error.pairingRequires.detail": "\u4E91\u7AEF\u670D\u52A1\u53EF\u4EE5\u662F\u5DF2\u90E8\u7F72\u4E14\u5065\u5EB7\u7684\uFF1B\u672C\u673A Link \u4ECD\u5FC5\u987B\u5148\u5411 Server \u7533\u8BF7\u77ED\u671F relay bootstrap token\uFF0C\u624D\u80FD\u518D\u5411 Relay \u7533\u8BF7 link_id\u3002"
|
|
@@ -459,6 +468,18 @@ function translateKnownError(message, language) {
|
|
|
459
468
|
if (serverHttp?.groups?.status) {
|
|
460
469
|
return translate(language, "error.serverHttp", { status: serverHttp.groups.status });
|
|
461
470
|
}
|
|
471
|
+
const pairingServerUnreachable = /^HermesPilot Server is unreachable while trying to [^.]+\. Please check whether (?<url>\S+) is reachable\./u.exec(message);
|
|
472
|
+
if (pairingServerUnreachable?.groups?.url) {
|
|
473
|
+
return translate(language, "error.pairingServerUnreachable", {
|
|
474
|
+
url: pairingServerUnreachable.groups.url
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
const pairingRelayUnreachable = /^Hermes Relay is unreachable while trying to [^.]+\. Please check whether (?<url>\S+) is reachable\./u.exec(message);
|
|
478
|
+
if (pairingRelayUnreachable?.groups?.url) {
|
|
479
|
+
return translate(language, "error.pairingRelayUnreachable", {
|
|
480
|
+
url: pairingRelayUnreachable.groups.url
|
|
481
|
+
});
|
|
482
|
+
}
|
|
462
483
|
if (message.includes("Pairing requires HermesPilot Server and Relay")) {
|
|
463
484
|
return [translate(language, "error.pairingRequires"), translate(language, "error.pairingRequires.detail")].join("\n");
|
|
464
485
|
}
|
|
@@ -487,6 +508,7 @@ async function assertPairingPreflightReady(options = {}) {
|
|
|
487
508
|
const configPath = resolveHermesConfigPath(profileName);
|
|
488
509
|
const envPath = path2.join(hermesHome, ".env");
|
|
489
510
|
const failures = [];
|
|
511
|
+
options.onProgress?.("hermes_files");
|
|
490
512
|
if (!await isDirectory(hermesHome)) {
|
|
491
513
|
failures.push({
|
|
492
514
|
code: "hermes_home_missing",
|
|
@@ -517,6 +539,23 @@ async function assertPairingPreflightReady(options = {}) {
|
|
|
517
539
|
if (failures.length > 0) {
|
|
518
540
|
throwPairingPreflightError(failures);
|
|
519
541
|
}
|
|
542
|
+
options.onProgress?.("hermes_cli");
|
|
543
|
+
let hermesVersion;
|
|
544
|
+
try {
|
|
545
|
+
const readVersion = options.readHermesVersion ?? readHermesVersion;
|
|
546
|
+
hermesVersion = await readVersion();
|
|
547
|
+
} catch {
|
|
548
|
+
throwPairingPreflightError([
|
|
549
|
+
{
|
|
550
|
+
code: "hermes_cli_unavailable",
|
|
551
|
+
zh: "\u6682\u65F6\u627E\u4E0D\u5230\u53EF\u7528\u7684 Hermes CLI \u547D\u4EE4\u3002",
|
|
552
|
+
en: "Hermes CLI is not available right now.",
|
|
553
|
+
actionZh: "\u8BF7\u786E\u8BA4 Hermes Link \u548C Hermes Agent \u5B89\u88C5\u5728\u540C\u4E00\u4E2A\u7CFB\u7EDF\u73AF\u5883\u4E2D\uFF0C\u5E76\u4E14\u7EC8\u7AEF\u80FD\u76F4\u63A5\u8FD0\u884C `hermes`\u3002\u5982\u679C\u4F60\u4F7F\u7528\u4E86\u81EA\u5B9A\u4E49\u5B89\u88C5\u8DEF\u5F84\uFF0C\u4E5F\u53EF\u4EE5\u8BBE\u7F6E HERMES_BIN\u3002",
|
|
554
|
+
actionEn: "Make sure Hermes Link and Hermes Agent are installed in the same system environment, and that the terminal can run `hermes` directly. If Hermes is installed in a custom location, set HERMES_BIN."
|
|
555
|
+
}
|
|
556
|
+
]);
|
|
557
|
+
}
|
|
558
|
+
options.onProgress?.("hermes_api_server");
|
|
520
559
|
const apiServerConfig = await readHermesApiServerConfig(profileName, configPath);
|
|
521
560
|
if (apiServerConfig.enabled !== true) {
|
|
522
561
|
throwPairingPreflightError([
|
|
@@ -548,6 +587,10 @@ async function assertPairingPreflightReady(options = {}) {
|
|
|
548
587
|
started: availability.started,
|
|
549
588
|
host: availability.configResult.apiServer.host ?? null,
|
|
550
589
|
port: availability.configResult.apiServer.port ?? null
|
|
590
|
+
},
|
|
591
|
+
hermesVersion: {
|
|
592
|
+
raw: hermesVersion.raw,
|
|
593
|
+
version: hermesVersion.version
|
|
551
594
|
}
|
|
552
595
|
};
|
|
553
596
|
} catch (error) {
|
|
@@ -780,16 +823,17 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
780
823
|
const language = languageChoice.language;
|
|
781
824
|
const t = translate.bind(null, language);
|
|
782
825
|
console.log(t("pair.preflight"));
|
|
783
|
-
const preflight = await assertPairingPreflightReady({ paths });
|
|
784
826
|
const environment = detectRuntimeEnvironment();
|
|
785
827
|
if (environment.warning) {
|
|
786
828
|
console.log(t("doctor.networkWarning", { message: environment.warning }));
|
|
787
829
|
}
|
|
788
|
-
|
|
789
|
-
|
|
830
|
+
await assertPairingPreflightReady({
|
|
831
|
+
paths,
|
|
832
|
+
onProgress: (stage) => {
|
|
833
|
+
console.log(t(pairingPreflightProgressKey(stage)));
|
|
834
|
+
}
|
|
835
|
+
});
|
|
790
836
|
console.log(t("pair.preparing"));
|
|
791
|
-
console.log(t("pair.server", { url: config.serverBaseUrl }));
|
|
792
|
-
console.log(t("pair.relay", { url: config.relayBaseUrl }));
|
|
793
837
|
await ensureIdentity(paths);
|
|
794
838
|
const hadActiveDevices = await hasActiveDevices(paths);
|
|
795
839
|
const probeBeforePair = await probeLocalLinkService({ port: config.port });
|
|
@@ -806,19 +850,12 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
806
850
|
const service = reusedRunningService ? null : await startLinkService({ paths });
|
|
807
851
|
const qrValue = JSON.stringify(prepared.qrPayload);
|
|
808
852
|
const pairingPageUrl = `http://127.0.0.1:${config.port}/pair?session_id=${encodeURIComponent(prepared.sessionId)}`;
|
|
809
|
-
console.log(t("pair.linkId", { value: prepared.linkId }));
|
|
810
|
-
console.log(t("pair.code", { value: prepared.code }));
|
|
811
|
-
console.log(t("pair.localApi", { port: config.port }));
|
|
812
|
-
if (!reusedRunningService) {
|
|
813
|
-
console.log(t("start.listening", { port: config.port }));
|
|
814
|
-
}
|
|
815
853
|
console.log(t("pair.scan"));
|
|
816
|
-
console.log(`Pairing page: ${pairingPageUrl}`);
|
|
817
|
-
const browserOpened = await openSystemBrowser(pairingPageUrl);
|
|
818
|
-
if (!browserOpened) {
|
|
819
|
-
console.log(t("pair.openBrowserFailed", { url: pairingPageUrl }));
|
|
820
|
-
}
|
|
821
854
|
qrcode.generate(qrValue, { small: true });
|
|
855
|
+
await openSystemBrowser(pairingPageUrl);
|
|
856
|
+
console.log(t("pair.openPairingPage", { url: pairingPageUrl }));
|
|
857
|
+
console.log(t("pair.manualCode"));
|
|
858
|
+
console.log(prepared.code);
|
|
822
859
|
console.log(t("pair.expires"));
|
|
823
860
|
const result = await waitForPairingOrShutdown(prepared.sessionId, paths);
|
|
824
861
|
if (service) {
|
|
@@ -923,6 +960,12 @@ program.command("doctor").description(helpText("doctor.description")).action(asy
|
|
|
923
960
|
console.log(`Hermes config backup: ${hermesConfig.backupPath}`);
|
|
924
961
|
}
|
|
925
962
|
}
|
|
963
|
+
try {
|
|
964
|
+
const hermesVersion = await readHermesVersion();
|
|
965
|
+
console.log(t("doctor.hermesCli", { value: formatHermesVersion(hermesVersion) }));
|
|
966
|
+
} catch {
|
|
967
|
+
console.log(t("doctor.hermesCliUnavailable"));
|
|
968
|
+
}
|
|
926
969
|
try {
|
|
927
970
|
const availability = await ensureHermesApiServerAvailable({ timeoutMs: 5e3 });
|
|
928
971
|
console.log(t(availability.started ? "doctor.apiStarted" : "doctor.apiReady"));
|
|
@@ -939,6 +982,19 @@ async function loadCliLanguage() {
|
|
|
939
982
|
const config = await loadConfig();
|
|
940
983
|
return resolveLanguage(config.language);
|
|
941
984
|
}
|
|
985
|
+
function formatHermesVersion(version) {
|
|
986
|
+
return version.version ?? version.raw;
|
|
987
|
+
}
|
|
988
|
+
function pairingPreflightProgressKey(stage) {
|
|
989
|
+
switch (stage) {
|
|
990
|
+
case "hermes_files":
|
|
991
|
+
return "pair.preflight.hermesFiles";
|
|
992
|
+
case "hermes_cli":
|
|
993
|
+
return "pair.preflight.hermesCli";
|
|
994
|
+
case "hermes_api_server":
|
|
995
|
+
return "pair.preflight.hermesApiServer";
|
|
996
|
+
}
|
|
997
|
+
}
|
|
942
998
|
async function deliverStagedFilesFromCli(stagingDir, paths, config) {
|
|
943
999
|
try {
|
|
944
1000
|
return await deliverStagedFilesThroughDaemon(stagingDir, config.port);
|
package/dist/http/app.js
CHANGED