@hermespilot/link 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-W4U6XJ4W.js → chunk-45RR27KS.js} +95 -38
- package/dist/cli/index.js +46 -12
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -3724,7 +3724,7 @@ import os2 from "os";
|
|
|
3724
3724
|
import path5 from "path";
|
|
3725
3725
|
|
|
3726
3726
|
// src/constants.ts
|
|
3727
|
-
var LINK_VERSION = "0.
|
|
3727
|
+
var LINK_VERSION = "0.3.0";
|
|
3728
3728
|
var LINK_COMMAND = "hermeslink";
|
|
3729
3729
|
var LINK_DEFAULT_PORT = 52379;
|
|
3730
3730
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -19122,35 +19122,49 @@ async function preparePairing(paths = resolveRuntimePaths()) {
|
|
|
19122
19122
|
public_key_pem: identity.public_key_pem
|
|
19123
19123
|
});
|
|
19124
19124
|
const relayBaseUrl = created.relayBaseUrl || config.relayBaseUrl;
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
|
|
19125
|
+
let assigned;
|
|
19126
|
+
let updatedIdentity;
|
|
19127
|
+
let routes;
|
|
19128
|
+
try {
|
|
19129
|
+
assigned = await bootstrapRelayLink({
|
|
19130
|
+
relayBaseUrl,
|
|
19131
|
+
relayBootstrapToken: created.relayBootstrapToken,
|
|
19132
|
+
identity,
|
|
19133
|
+
paths
|
|
19134
|
+
});
|
|
19135
|
+
updatedIdentity = await loadIdentity(paths) ?? identity;
|
|
19136
|
+
routes = await discoverRouteCandidates({
|
|
19137
|
+
port: config.port,
|
|
19138
|
+
relayBaseUrl,
|
|
19139
|
+
relayBootstrapToken: created.relayBootstrapToken,
|
|
19140
|
+
configuredLanHost: config.lanHost,
|
|
19141
|
+
linkId: assigned.linkId,
|
|
19142
|
+
installId: updatedIdentity.install_id,
|
|
19143
|
+
publicKeyPem: updatedIdentity.public_key_pem
|
|
19144
|
+
});
|
|
19145
|
+
await patchServerJson(config.serverBaseUrl, `/api/v1/link-pairings/${created.sessionId}/link`, created.pairingToken, {
|
|
19146
|
+
install_id: updatedIdentity.install_id,
|
|
19147
|
+
link_id: assigned.linkId,
|
|
19148
|
+
link_version: LINK_VERSION,
|
|
19149
|
+
display_name: systemInfo.defaultDisplayName,
|
|
19150
|
+
platform: systemInfo.platform,
|
|
19151
|
+
hostname: systemInfo.hostname ?? void 0,
|
|
19152
|
+
lan_ips: routes.lanIps,
|
|
19153
|
+
public_ipv4s: routes.publicIpv4s,
|
|
19154
|
+
public_ipv6s: routes.publicIpv6s,
|
|
19155
|
+
preferred_urls: routes.preferredUrls,
|
|
19156
|
+
environment: routes.environment
|
|
19157
|
+
});
|
|
19158
|
+
} catch (error) {
|
|
19159
|
+
await reportPairingErrorToServer({
|
|
19160
|
+
serverBaseUrl: config.serverBaseUrl,
|
|
19161
|
+
sessionId: created.sessionId,
|
|
19162
|
+
source: "link",
|
|
19163
|
+
pairingToken: created.pairingToken,
|
|
19164
|
+
error: pairingErrorSnapshot("prepare_pairing", error)
|
|
19165
|
+
});
|
|
19166
|
+
throw error;
|
|
19167
|
+
}
|
|
19154
19168
|
const qrPayload = {
|
|
19155
19169
|
kind: "hermes_link_pairing",
|
|
19156
19170
|
version: 1,
|
|
@@ -19241,14 +19255,26 @@ async function clearPairingClaim(sessionId, paths = resolveRuntimePaths()) {
|
|
|
19241
19255
|
async function claimPairing(input) {
|
|
19242
19256
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
19243
19257
|
const [identity, config] = await Promise.all([loadRequiredIdentity2(paths), loadConfig(paths)]);
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
|
|
19258
|
+
let verified;
|
|
19259
|
+
try {
|
|
19260
|
+
verified = await postServerJson(
|
|
19261
|
+
config.serverBaseUrl,
|
|
19262
|
+
`/api/v1/link-pairings/${input.sessionId}/claim/verify`,
|
|
19263
|
+
{
|
|
19264
|
+
claim_token: input.claimToken,
|
|
19265
|
+
app_instance_id: input.appInstanceId ?? void 0
|
|
19266
|
+
}
|
|
19267
|
+
);
|
|
19268
|
+
} catch (error) {
|
|
19269
|
+
await reportPairingErrorToServer({
|
|
19270
|
+
serverBaseUrl: config.serverBaseUrl,
|
|
19271
|
+
sessionId: input.sessionId,
|
|
19272
|
+
source: "link",
|
|
19273
|
+
claimToken: input.claimToken,
|
|
19274
|
+
error: pairingErrorSnapshot("claim_verify", error)
|
|
19275
|
+
});
|
|
19276
|
+
throw error;
|
|
19277
|
+
}
|
|
19252
19278
|
if (verified.ok !== true || verified.linkId !== identity.link_id) {
|
|
19253
19279
|
throw new LinkHttpError(409, "pairing_claim_mismatch", "Pairing claim does not match this Link");
|
|
19254
19280
|
}
|
|
@@ -19295,6 +19321,36 @@ async function postServerJson(serverBaseUrl, path24, body) {
|
|
|
19295
19321
|
});
|
|
19296
19322
|
return readJsonResponse2(response);
|
|
19297
19323
|
}
|
|
19324
|
+
async function reportPairingErrorToServer(input) {
|
|
19325
|
+
const body = {
|
|
19326
|
+
source: input.source,
|
|
19327
|
+
error: input.error
|
|
19328
|
+
};
|
|
19329
|
+
if (input.claimToken) {
|
|
19330
|
+
body.claim_token = input.claimToken;
|
|
19331
|
+
}
|
|
19332
|
+
if (input.pairingToken) {
|
|
19333
|
+
body.pairing_token = input.pairingToken;
|
|
19334
|
+
}
|
|
19335
|
+
await fetch(`${input.serverBaseUrl.replace(/\/+$/u, "")}/api/v1/link-pairings/${encodeURIComponent(input.sessionId)}/error`, {
|
|
19336
|
+
method: "POST",
|
|
19337
|
+
headers: {
|
|
19338
|
+
accept: "application/json",
|
|
19339
|
+
"content-type": "application/json",
|
|
19340
|
+
...input.pairingToken ? { authorization: `Bearer ${input.pairingToken}` } : {}
|
|
19341
|
+
},
|
|
19342
|
+
signal: AbortSignal.timeout(3e3),
|
|
19343
|
+
body: JSON.stringify(body)
|
|
19344
|
+
}).catch(() => void 0);
|
|
19345
|
+
}
|
|
19346
|
+
function pairingErrorSnapshot(stage, error) {
|
|
19347
|
+
return {
|
|
19348
|
+
stage,
|
|
19349
|
+
message: error instanceof Error ? error.message : String(error),
|
|
19350
|
+
error_name: error instanceof Error ? error.name : void 0,
|
|
19351
|
+
occurred_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
19352
|
+
};
|
|
19353
|
+
}
|
|
19298
19354
|
async function patchServerJson(serverBaseUrl, path24, token, body) {
|
|
19299
19355
|
const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path24}`, {
|
|
19300
19356
|
method: "PATCH",
|
|
@@ -20436,6 +20492,7 @@ export {
|
|
|
20436
20492
|
resolveRuntimePaths,
|
|
20437
20493
|
getLinkLogFile,
|
|
20438
20494
|
ensureHermesApiServerAvailable,
|
|
20495
|
+
readHermesVersion,
|
|
20439
20496
|
loadConfig,
|
|
20440
20497
|
saveConfig,
|
|
20441
20498
|
normalizeLanHost,
|
package/dist/cli/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
preparePairing,
|
|
21
21
|
probeLocalLinkService,
|
|
22
22
|
readHermesApiServerConfig,
|
|
23
|
+
readHermesVersion,
|
|
23
24
|
readPairingClaim,
|
|
24
25
|
reportLinkStatusToServer,
|
|
25
26
|
resolveHermesConfigPath,
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
startDaemonProcess,
|
|
31
32
|
startLinkService,
|
|
32
33
|
stopDaemonProcess
|
|
33
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-45RR27KS.js";
|
|
34
35
|
|
|
35
36
|
// src/cli/index.ts
|
|
36
37
|
import { Command } from "commander";
|
|
@@ -277,6 +278,7 @@ var messages = {
|
|
|
277
278
|
"pair.code": "Pairing code: {value}",
|
|
278
279
|
"pair.localApi": "Local API: http://127.0.0.1:{port}",
|
|
279
280
|
"pair.scan": "Open this pairing page in the HermesPilot App or scan the QR code below:",
|
|
281
|
+
"pair.openBrowserFailed": "Could not open the system browser automatically. You can still scan the QR code below or open this URL manually: {url}",
|
|
280
282
|
"pair.expires": "Pairing expires in 10 minutes. Press Ctrl+C to cancel waiting.",
|
|
281
283
|
"pair.claimed": "Pairing succeeded. Starting Hermes Link in the background...",
|
|
282
284
|
"pair.claimedRunning": "Pairing succeeded. Hermes Link is already running in the background.",
|
|
@@ -363,6 +365,7 @@ var messages = {
|
|
|
363
365
|
"pair.code": "\u914D\u5BF9\u7801\uFF1A{value}",
|
|
364
366
|
"pair.localApi": "\u672C\u5730 API\uFF1Ahttp://127.0.0.1:{port}",
|
|
365
367
|
"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",
|
|
368
|
+
"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}",
|
|
366
369
|
"pair.expires": "\u914D\u5BF9\u4F1A\u8BDD 10 \u5206\u949F\u540E\u8FC7\u671F\u3002\u6309 Ctrl+C \u9000\u51FA\u7B49\u5F85\u3002",
|
|
367
370
|
"pair.claimed": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002\u6B63\u5728\u628A Hermes Link \u5207\u6362\u5230\u540E\u53F0\u8FD0\u884C...",
|
|
368
371
|
"pair.claimedRunning": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002Hermes Link \u5DF2\u5728\u540E\u53F0\u6301\u7EED\u8FD0\u884C\u3002",
|
|
@@ -507,6 +510,21 @@ async function assertPairingPreflightReady(options = {}) {
|
|
|
507
510
|
if (failures.length > 0) {
|
|
508
511
|
throwPairingPreflightError(failures);
|
|
509
512
|
}
|
|
513
|
+
const readVersion = options.readHermesVersion ?? readHermesVersion;
|
|
514
|
+
try {
|
|
515
|
+
await readVersion();
|
|
516
|
+
} catch (error) {
|
|
517
|
+
throwPairingPreflightError([
|
|
518
|
+
{
|
|
519
|
+
code: "hermes_cli_unavailable",
|
|
520
|
+
zh: "\u6CA1\u6709\u627E\u5230\u53EF\u7528\u7684 Hermes Agent CLI\u3002Link \u9700\u8981\u548C Hermes \u5728\u540C\u4E00\u4E2A\u7CFB\u7EDF\u73AF\u5883\u91CC\uFF0C\u624D\u80FD\u5728\u914D\u5BF9\u65F6\u542F\u52A8\u548C\u68C0\u6D4B Hermes Gateway\u3002",
|
|
521
|
+
en: "Hermes Agent CLI was not found. Link needs Hermes to be available in the same system environment so it can start and check Hermes Gateway during pairing.",
|
|
522
|
+
actionZh: "\u8BF7\u628A Hermes Agent \u5B89\u88C5\u5728\u8FD9\u53F0\u5BBF\u4E3B\u673A\u4E0A\uFF0C\u6216\u628A `HERMES_BIN` \u6307\u5411 Link \u53EF\u4EE5\u76F4\u63A5\u6267\u884C\u7684 Hermes CLI\u3002\u5F53\u524D\u5982\u679C Hermes \u53EA\u5728\u53E6\u4E00\u4E2A Docker\u3001WSL \u6216\u865A\u62DF\u673A\u73AF\u5883\u91CC\uFF0C\u5BBF\u4E3B\u673A Link \u4E0D\u80FD\u76F4\u63A5\u7528\u5B83\u5B8C\u6210\u914D\u5BF9\u524D\u68C0\u67E5\u3002",
|
|
523
|
+
actionEn: "Install Hermes Agent on this host, or point `HERMES_BIN` to a Hermes CLI that Link can run directly in this system environment. If Hermes only exists inside another Docker, WSL, or VM environment, host Link cannot use it for pairing preflight.",
|
|
524
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
525
|
+
}
|
|
526
|
+
]);
|
|
527
|
+
}
|
|
510
528
|
const apiServerConfig = await readHermesApiServerConfig(profileName, configPath);
|
|
511
529
|
if (apiServerConfig.enabled !== true) {
|
|
512
530
|
throwPairingPreflightError([
|
|
@@ -609,16 +627,29 @@ async function openSystemBrowser(url) {
|
|
|
609
627
|
return await spawnDetached("xdg-open", [url]);
|
|
610
628
|
}
|
|
611
629
|
async function spawnDetached(command, args) {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
630
|
+
return await new Promise((resolve) => {
|
|
631
|
+
let settled = false;
|
|
632
|
+
const settle = (ok) => {
|
|
633
|
+
if (settled) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
settled = true;
|
|
637
|
+
resolve(ok);
|
|
638
|
+
};
|
|
639
|
+
try {
|
|
640
|
+
const child = spawn(command, args, {
|
|
641
|
+
detached: true,
|
|
642
|
+
stdio: "ignore"
|
|
643
|
+
});
|
|
644
|
+
child.once("error", () => settle(false));
|
|
645
|
+
child.once("spawn", () => {
|
|
646
|
+
child.unref();
|
|
647
|
+
settle(true);
|
|
648
|
+
});
|
|
649
|
+
} catch {
|
|
650
|
+
settle(false);
|
|
651
|
+
}
|
|
652
|
+
});
|
|
622
653
|
}
|
|
623
654
|
|
|
624
655
|
// src/cli/index.ts
|
|
@@ -777,7 +808,10 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
777
808
|
}
|
|
778
809
|
console.log(t("pair.scan"));
|
|
779
810
|
console.log(`Pairing page: ${pairingPageUrl}`);
|
|
780
|
-
|
|
811
|
+
const browserOpened = await openSystemBrowser(pairingPageUrl);
|
|
812
|
+
if (!browserOpened) {
|
|
813
|
+
console.log(t("pair.openBrowserFailed", { url: pairingPageUrl }));
|
|
814
|
+
}
|
|
781
815
|
qrcode.generate(qrValue, { small: true });
|
|
782
816
|
console.log(t("pair.expires"));
|
|
783
817
|
const result = await waitForPairingOrShutdown(prepared.sessionId, paths);
|
package/dist/http/app.js
CHANGED