@hermespilot/link 0.2.9 → 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.
@@ -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.2.9";
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
- const assigned = await bootstrapRelayLink({
19126
- relayBaseUrl,
19127
- relayBootstrapToken: created.relayBootstrapToken,
19128
- identity,
19129
- paths
19130
- });
19131
- const updatedIdentity = await loadIdentity(paths) ?? identity;
19132
- const routes = await discoverRouteCandidates({
19133
- port: config.port,
19134
- relayBaseUrl,
19135
- relayBootstrapToken: created.relayBootstrapToken,
19136
- configuredLanHost: config.lanHost,
19137
- linkId: assigned.linkId,
19138
- installId: updatedIdentity.install_id,
19139
- publicKeyPem: updatedIdentity.public_key_pem
19140
- });
19141
- await patchServerJson(config.serverBaseUrl, `/api/v1/link-pairings/${created.sessionId}/link`, created.pairingToken, {
19142
- install_id: updatedIdentity.install_id,
19143
- link_id: assigned.linkId,
19144
- link_version: LINK_VERSION,
19145
- display_name: systemInfo.defaultDisplayName,
19146
- platform: systemInfo.platform,
19147
- hostname: systemInfo.hostname ?? void 0,
19148
- lan_ips: routes.lanIps,
19149
- public_ipv4s: routes.publicIpv4s,
19150
- public_ipv6s: routes.publicIpv6s,
19151
- preferred_urls: routes.preferredUrls,
19152
- environment: routes.environment
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
- const verified = await postServerJson(
19245
- config.serverBaseUrl,
19246
- `/api/v1/link-pairings/${input.sessionId}/claim/verify`,
19247
- {
19248
- claim_token: input.claimToken,
19249
- app_instance_id: input.appInstanceId ?? void 0
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-VLTX75SY.js";
34
+ } from "../chunk-45RR27KS.js";
34
35
 
35
36
  // src/cli/index.ts
36
37
  import { Command } from "commander";
@@ -509,6 +510,21 @@ async function assertPairingPreflightReady(options = {}) {
509
510
  if (failures.length > 0) {
510
511
  throwPairingPreflightError(failures);
511
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
+ }
512
528
  const apiServerConfig = await readHermesApiServerConfig(profileName, configPath);
513
529
  if (apiServerConfig.enabled !== true) {
514
530
  throwPairingPreflightError([
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "../chunk-VLTX75SY.js";
3
+ } from "../chunk-45RR27KS.js";
4
4
  export {
5
5
  createApp
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hermespilot/link",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Hermes Link companion service and CLI for connecting hermes-agent through HermesPilot",
6
6
  "license": "MIT",