@hermespilot/link 0.4.0 → 0.4.1
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-FWPHQZP6.js → chunk-476X63MC.js} +39 -21
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +30 -8
- package/dist/http/app.d.ts +1 -15
- package/dist/http/app.js +1 -1
- package/dist/paths-CmAiZsna.d.ts +16 -0
- package/package.json +1 -1
|
@@ -1500,6 +1500,13 @@ function resolveHermesProfileDir(profileName = "default") {
|
|
|
1500
1500
|
}
|
|
1501
1501
|
return path3.join(os.homedir(), ".hermes", "profiles", profileName);
|
|
1502
1502
|
}
|
|
1503
|
+
function resolveHermesProfilesDir() {
|
|
1504
|
+
const hermesHome = process.env.HERMES_HOME?.trim();
|
|
1505
|
+
if (hermesHome) {
|
|
1506
|
+
return path3.join(resolveDefaultHermesRoot(path3.resolve(hermesHome)), "profiles");
|
|
1507
|
+
}
|
|
1508
|
+
return path3.join(os.homedir(), ".hermes", "profiles");
|
|
1509
|
+
}
|
|
1503
1510
|
function resolveHermesConfigPath(profileName = "default") {
|
|
1504
1511
|
return path3.join(resolveHermesProfileDir(profileName), "config.yaml");
|
|
1505
1512
|
}
|
|
@@ -3970,7 +3977,7 @@ import os2 from "os";
|
|
|
3970
3977
|
import path5 from "path";
|
|
3971
3978
|
|
|
3972
3979
|
// src/constants.ts
|
|
3973
|
-
var LINK_VERSION = "0.4.
|
|
3980
|
+
var LINK_VERSION = "0.4.1";
|
|
3974
3981
|
var LINK_COMMAND = "hermeslink";
|
|
3975
3982
|
var LINK_DEFAULT_PORT = 52379;
|
|
3976
3983
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -9253,7 +9260,6 @@ function isNodeError9(error, code) {
|
|
|
9253
9260
|
// src/conversations/hermes-session-sync.ts
|
|
9254
9261
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
9255
9262
|
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "fs/promises";
|
|
9256
|
-
import os4 from "os";
|
|
9257
9263
|
import path15 from "path";
|
|
9258
9264
|
|
|
9259
9265
|
// src/conversations/delivery-import.ts
|
|
@@ -10616,7 +10622,7 @@ function rememberKnownHermesConversation(map, sessionId, conversationId) {
|
|
|
10616
10622
|
}
|
|
10617
10623
|
async function discoverHermesProfileNames() {
|
|
10618
10624
|
const names = /* @__PURE__ */ new Set([DEFAULT_PROFILE_NAME]);
|
|
10619
|
-
const profilesDir =
|
|
10625
|
+
const profilesDir = resolveHermesProfilesDir();
|
|
10620
10626
|
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
10621
10627
|
(error) => {
|
|
10622
10628
|
if (isNodeError11(error, "ENOENT")) {
|
|
@@ -15760,7 +15766,6 @@ function createHttpErrorMiddleware(logger) {
|
|
|
15760
15766
|
|
|
15761
15767
|
// src/hermes/profiles.ts
|
|
15762
15768
|
import { readdir as readdir9, readFile as readFile12, rename as rename3, rm as rm6, stat as stat12 } from "fs/promises";
|
|
15763
|
-
import os5 from "os";
|
|
15764
15769
|
import path18 from "path";
|
|
15765
15770
|
import YAML2 from "yaml";
|
|
15766
15771
|
var DEFAULT_PROFILE = "default";
|
|
@@ -15768,7 +15773,7 @@ var PROFILE_NAME_PATTERN4 = /^[a-zA-Z0-9._-]{1,64}$/;
|
|
|
15768
15773
|
async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
15769
15774
|
const profiles = /* @__PURE__ */ new Map();
|
|
15770
15775
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
15771
|
-
const profilesDir =
|
|
15776
|
+
const profilesDir = resolveHermesProfilesDir();
|
|
15772
15777
|
const entries = await readdir9(profilesDir, { withFileTypes: true }).catch(
|
|
15773
15778
|
(error) => {
|
|
15774
15779
|
if (isNodeError14(error, "ENOENT")) {
|
|
@@ -20624,7 +20629,7 @@ async function handleFrame(socket, raw, localPort, abortControllers) {
|
|
|
20624
20629
|
// src/runtime/system-info.ts
|
|
20625
20630
|
import { execFileSync } from "child_process";
|
|
20626
20631
|
import { readFileSync } from "fs";
|
|
20627
|
-
import
|
|
20632
|
+
import os4 from "os";
|
|
20628
20633
|
function readLinkSystemInfo() {
|
|
20629
20634
|
const platform = process.platform;
|
|
20630
20635
|
const hostname = readHostname(platform);
|
|
@@ -20663,7 +20668,7 @@ function readHostname(platform) {
|
|
|
20663
20668
|
return computerName;
|
|
20664
20669
|
}
|
|
20665
20670
|
}
|
|
20666
|
-
return normalizeText(
|
|
20671
|
+
return normalizeText(os4.hostname());
|
|
20667
20672
|
}
|
|
20668
20673
|
function readOsLabel(platform) {
|
|
20669
20674
|
if (platform === "darwin") {
|
|
@@ -20671,12 +20676,12 @@ function readOsLabel(platform) {
|
|
|
20671
20676
|
return version ? `macOS ${version}` : "macOS";
|
|
20672
20677
|
}
|
|
20673
20678
|
if (platform === "linux") {
|
|
20674
|
-
return readLinuxOsRelease() ?? `Linux ${
|
|
20679
|
+
return readLinuxOsRelease() ?? `Linux ${os4.release()}`;
|
|
20675
20680
|
}
|
|
20676
20681
|
if (platform === "win32") {
|
|
20677
|
-
return `Windows ${
|
|
20682
|
+
return `Windows ${os4.release()}`;
|
|
20678
20683
|
}
|
|
20679
|
-
return `${
|
|
20684
|
+
return `${os4.type()} ${os4.release()}`.trim();
|
|
20680
20685
|
}
|
|
20681
20686
|
function readLinuxOsRelease() {
|
|
20682
20687
|
for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
|
|
@@ -20723,11 +20728,11 @@ function truncateText(value, maxLength) {
|
|
|
20723
20728
|
}
|
|
20724
20729
|
|
|
20725
20730
|
// src/topology/network.ts
|
|
20726
|
-
import
|
|
20731
|
+
import os6 from "os";
|
|
20727
20732
|
|
|
20728
20733
|
// src/topology/environment.ts
|
|
20729
20734
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
20730
|
-
import
|
|
20735
|
+
import os5 from "os";
|
|
20731
20736
|
function detectRuntimeEnvironment(env = process.env) {
|
|
20732
20737
|
if (isWsl(env)) {
|
|
20733
20738
|
return {
|
|
@@ -20756,7 +20761,7 @@ function isWsl(env) {
|
|
|
20756
20761
|
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
|
|
20757
20762
|
return true;
|
|
20758
20763
|
}
|
|
20759
|
-
const release =
|
|
20764
|
+
const release = os5.release().toLowerCase();
|
|
20760
20765
|
return release.includes("microsoft") || release.includes("wsl");
|
|
20761
20766
|
}
|
|
20762
20767
|
function isContainer(env) {
|
|
@@ -20801,7 +20806,7 @@ async function discoverRouteCandidates(options) {
|
|
|
20801
20806
|
};
|
|
20802
20807
|
}
|
|
20803
20808
|
function discoverLanIps() {
|
|
20804
|
-
return discoverLanIpsFromInterfaces(
|
|
20809
|
+
return discoverLanIpsFromInterfaces(os6.networkInterfaces());
|
|
20805
20810
|
}
|
|
20806
20811
|
function discoverLanIpsFromInterfaces(interfaces) {
|
|
20807
20812
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -22408,6 +22413,10 @@ async function readPairingSession(sessionId, paths = resolveRuntimePaths()) {
|
|
|
22408
22413
|
expires_at: record.expires_at
|
|
22409
22414
|
};
|
|
22410
22415
|
}
|
|
22416
|
+
function isPairingSessionExpired(session) {
|
|
22417
|
+
const expiresAtMs = Date.parse(session.expires_at);
|
|
22418
|
+
return !Number.isFinite(expiresAtMs) || Date.now() >= expiresAtMs;
|
|
22419
|
+
}
|
|
22411
22420
|
async function recordPairingClaim(input, paths = resolveRuntimePaths()) {
|
|
22412
22421
|
const record = {
|
|
22413
22422
|
session_id: input.sessionId,
|
|
@@ -22437,7 +22446,20 @@ async function clearPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
|
22437
22446
|
}
|
|
22438
22447
|
async function claimPairing(input) {
|
|
22439
22448
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
22440
|
-
const [identity, config] = await Promise.all([
|
|
22449
|
+
const [identity, config, localSession] = await Promise.all([
|
|
22450
|
+
loadRequiredIdentity2(paths),
|
|
22451
|
+
loadConfig(paths),
|
|
22452
|
+
readPairingSession(input.sessionId, paths)
|
|
22453
|
+
]);
|
|
22454
|
+
if (!localSession) {
|
|
22455
|
+
throw new LinkHttpError(404, "pairing_session_not_found", "Pairing session was not found");
|
|
22456
|
+
}
|
|
22457
|
+
if (isPairingSessionExpired(localSession)) {
|
|
22458
|
+
throw new LinkHttpError(404, "pairing_session_expired", "Pairing session has expired");
|
|
22459
|
+
}
|
|
22460
|
+
if (localSession.link_id !== identity.link_id) {
|
|
22461
|
+
throw new LinkHttpError(409, "pairing_claim_mismatch", "Pairing claim does not match this Link");
|
|
22462
|
+
}
|
|
22441
22463
|
let verified;
|
|
22442
22464
|
try {
|
|
22443
22465
|
verified = await postServerJson(
|
|
@@ -23251,7 +23273,7 @@ function registerPairingRoutes(router, options) {
|
|
|
23251
23273
|
throw new LinkHttpError(404, "pairing_session_not_found", "Pairing session was not found");
|
|
23252
23274
|
}
|
|
23253
23275
|
const state = await readPairingState(sessionId, paths);
|
|
23254
|
-
if (!state.claimed &&
|
|
23276
|
+
if (!state.claimed && isPairingSessionExpired(session)) {
|
|
23255
23277
|
throw new LinkHttpError(404, "pairing_session_expired", "Pairing session has expired");
|
|
23256
23278
|
}
|
|
23257
23279
|
ctx.set("cache-control", "no-store");
|
|
@@ -23276,7 +23298,7 @@ async function readPairingState(sessionId, paths) {
|
|
|
23276
23298
|
async function renderPairingPage(input) {
|
|
23277
23299
|
const session = input.session;
|
|
23278
23300
|
const expiresAtMs = Date.parse(session.expires_at);
|
|
23279
|
-
const isExpired = !input.state.claimed &&
|
|
23301
|
+
const isExpired = !input.state.claimed && isPairingSessionExpired(session);
|
|
23280
23302
|
const qrPayload = JSON.stringify({
|
|
23281
23303
|
kind: "hermes_link_pairing",
|
|
23282
23304
|
version: 1,
|
|
@@ -23687,10 +23709,6 @@ function formatDate(value) {
|
|
|
23687
23709
|
const date = new Date(value);
|
|
23688
23710
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
|
|
23689
23711
|
}
|
|
23690
|
-
function isExpiredAt(value) {
|
|
23691
|
-
const expiresAtMs = Date.parse(value);
|
|
23692
|
-
return Number.isFinite(expiresAtMs) && Date.now() >= expiresAtMs;
|
|
23693
|
-
}
|
|
23694
23712
|
|
|
23695
23713
|
// src/http/routes/internal.ts
|
|
23696
23714
|
function registerInternalRoutes(router, options) {
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -34,10 +34,11 @@ import {
|
|
|
34
34
|
startDaemonProcess,
|
|
35
35
|
startLinkService,
|
|
36
36
|
stopDaemonProcess
|
|
37
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-476X63MC.js";
|
|
38
38
|
|
|
39
39
|
// src/cli/index.ts
|
|
40
40
|
import { Command } from "commander";
|
|
41
|
+
import path3 from "path";
|
|
41
42
|
import { createInterface } from "readline/promises";
|
|
42
43
|
import qrcode from "qrcode-terminal";
|
|
43
44
|
|
|
@@ -286,6 +287,7 @@ var messages = {
|
|
|
286
287
|
"pair.openPairingPage": "If the QR code is hard to scan, you can open this page locally: {url}",
|
|
287
288
|
"pair.manualCode": "You can also use the HermesPilot App manual connection mode and enter this pairing code:",
|
|
288
289
|
"pair.expires": "Pairing expires in 10 minutes. Press Ctrl+C to cancel waiting.",
|
|
290
|
+
"pair.expired": "Pairing expired. Please run `hermeslink pair` again.",
|
|
289
291
|
"pair.claimed": "Pairing succeeded. Starting Hermes Link in the background...",
|
|
290
292
|
"pair.claimedRunning": "Pairing succeeded. Hermes Link is already running in the background.",
|
|
291
293
|
"pair.relayOnlyNotice": "Network note: this {kind} environment does not expose a phone-reachable LAN/public direct address by default. The App will connect through Relay.",
|
|
@@ -380,6 +382,7 @@ var messages = {
|
|
|
380
382
|
"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}",
|
|
381
383
|
"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",
|
|
382
384
|
"pair.expires": "\u914D\u5BF9\u4F1A\u8BDD 10 \u5206\u949F\u540E\u8FC7\u671F\u3002\u6309 Ctrl+C \u9000\u51FA\u7B49\u5F85\u3002",
|
|
385
|
+
"pair.expired": "\u914D\u5BF9\u4F1A\u8BDD\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C `hermeslink pair`\u3002",
|
|
383
386
|
"pair.claimed": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002\u6B63\u5728\u628A Hermes Link \u5207\u6362\u5230\u540E\u53F0\u8FD0\u884C...",
|
|
384
387
|
"pair.claimedRunning": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002Hermes Link \u5DF2\u5728\u540E\u53F0\u6301\u7EED\u8FD0\u884C\u3002",
|
|
385
388
|
"pair.relayOnlyNotice": "\u7F51\u7EDC\u63D0\u793A\uFF1A\u5F53\u524D\u662F {kind} \u73AF\u5883\uFF0C\u9ED8\u8BA4\u4E0D\u4F1A\u66B4\u9732\u624B\u673A\u53EF\u8BBF\u95EE\u7684\u5C40\u57DF\u7F51\u6216\u516C\u7F51\u76F4\u8FDE\u5730\u5740\u3002App \u4F1A\u901A\u8FC7 Relay \u8FDE\u63A5\u3002",
|
|
@@ -871,7 +874,11 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
871
874
|
console.log(t("pair.manualCode"));
|
|
872
875
|
console.log(prepared.code);
|
|
873
876
|
console.log(t("pair.expires"));
|
|
874
|
-
const result = await waitForPairingOrShutdown(
|
|
877
|
+
const result = await waitForPairingOrShutdown(
|
|
878
|
+
prepared.sessionId,
|
|
879
|
+
prepared.expiresAt,
|
|
880
|
+
paths
|
|
881
|
+
);
|
|
875
882
|
if (service) {
|
|
876
883
|
await service.close();
|
|
877
884
|
}
|
|
@@ -910,6 +917,8 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
910
917
|
const status = await startDaemonProcess(paths);
|
|
911
918
|
console.log(t("start.backgroundStarted", { pid: status.pid ?? "unknown" }));
|
|
912
919
|
}
|
|
920
|
+
} else if (result === "expired") {
|
|
921
|
+
console.log(t("pair.expired"));
|
|
913
922
|
}
|
|
914
923
|
} finally {
|
|
915
924
|
pairingRelayBridge?.close();
|
|
@@ -990,11 +999,13 @@ program.command("doctor").description(helpText("doctor.description")).action(asy
|
|
|
990
999
|
console.log(t("doctor.apiUnavailable", { message: error instanceof Error ? error.message : String(error) }));
|
|
991
1000
|
}
|
|
992
1001
|
});
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1002
|
+
if (isCliEntrypoint()) {
|
|
1003
|
+
program.parseAsync(process.argv).catch(async (error) => {
|
|
1004
|
+
const language = await loadCliLanguage().catch(() => detectSystemLanguage());
|
|
1005
|
+
console.error(localizeErrorMessage(error, language));
|
|
1006
|
+
process.exitCode = 1;
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
998
1009
|
async function loadCliLanguage() {
|
|
999
1010
|
const config = await loadConfig();
|
|
1000
1011
|
return resolveLanguage(config.language);
|
|
@@ -1100,8 +1111,9 @@ async function waitForShutdown(cleanup) {
|
|
|
1100
1111
|
});
|
|
1101
1112
|
await cleanup();
|
|
1102
1113
|
}
|
|
1103
|
-
async function waitForPairingOrShutdown(sessionId, paths) {
|
|
1114
|
+
async function waitForPairingOrShutdown(sessionId, expiresAt, paths) {
|
|
1104
1115
|
let shutdownRequested = false;
|
|
1116
|
+
const expiresAtMs = Date.parse(expiresAt);
|
|
1105
1117
|
const stop = () => {
|
|
1106
1118
|
shutdownRequested = true;
|
|
1107
1119
|
};
|
|
@@ -1109,6 +1121,9 @@ async function waitForPairingOrShutdown(sessionId, paths) {
|
|
|
1109
1121
|
process.once("SIGTERM", stop);
|
|
1110
1122
|
try {
|
|
1111
1123
|
while (!shutdownRequested) {
|
|
1124
|
+
if (Number.isFinite(expiresAtMs) && Date.now() >= expiresAtMs) {
|
|
1125
|
+
return "expired";
|
|
1126
|
+
}
|
|
1112
1127
|
const record = await readPairingClaim(sessionId, paths);
|
|
1113
1128
|
if (record) {
|
|
1114
1129
|
return "claimed";
|
|
@@ -1141,3 +1156,10 @@ function printPostPairingNetworkNotice(environment, config, t) {
|
|
|
1141
1156
|
console.log(t("pair.relayOnlyLanHostHint"));
|
|
1142
1157
|
console.log(t("pair.relayOnlySafetyHint"));
|
|
1143
1158
|
}
|
|
1159
|
+
function isCliEntrypoint() {
|
|
1160
|
+
const entry = process.argv[1];
|
|
1161
|
+
return Boolean(entry && import.meta.url === new URL(`file://${path3.resolve(entry)}`).href);
|
|
1162
|
+
}
|
|
1163
|
+
export {
|
|
1164
|
+
waitForPairingOrShutdown
|
|
1165
|
+
};
|
package/dist/http/app.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Koa from 'koa';
|
|
2
|
+
import { R as RuntimePaths } from '../paths-CmAiZsna.js';
|
|
2
3
|
|
|
3
4
|
interface ConversationProfileSummary {
|
|
4
5
|
uid?: string;
|
|
@@ -267,21 +268,6 @@ interface CancelRunResult {
|
|
|
267
268
|
}
|
|
268
269
|
type ConversationEventListener = (event: ConversationEvent) => void;
|
|
269
270
|
|
|
270
|
-
interface RuntimePaths {
|
|
271
|
-
homeDir: string;
|
|
272
|
-
identityFile: string;
|
|
273
|
-
configFile: string;
|
|
274
|
-
stateFile: string;
|
|
275
|
-
credentialsFile: string;
|
|
276
|
-
databaseFile: string;
|
|
277
|
-
conversationsDir: string;
|
|
278
|
-
blobsDir: string;
|
|
279
|
-
indexesDir: string;
|
|
280
|
-
logsDir: string;
|
|
281
|
-
runDir: string;
|
|
282
|
-
pairingDir: string;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
271
|
interface LinkStatistics {
|
|
286
272
|
conversations: {
|
|
287
273
|
total: number;
|
package/dist/http/app.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface RuntimePaths {
|
|
2
|
+
homeDir: string;
|
|
3
|
+
identityFile: string;
|
|
4
|
+
configFile: string;
|
|
5
|
+
stateFile: string;
|
|
6
|
+
credentialsFile: string;
|
|
7
|
+
databaseFile: string;
|
|
8
|
+
conversationsDir: string;
|
|
9
|
+
blobsDir: string;
|
|
10
|
+
indexesDir: string;
|
|
11
|
+
logsDir: string;
|
|
12
|
+
runDir: string;
|
|
13
|
+
pairingDir: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type { RuntimePaths as R };
|