@hermespilot/link 0.1.4 → 0.1.5

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/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  LINK_COMMAND,
4
4
  LINK_VERSION,
5
+ clearPairingClaim,
5
6
  createApp,
6
7
  createFileLogger,
7
8
  ensureHermesApiServerKey,
@@ -11,8 +12,9 @@ import {
11
12
  loadConfig,
12
13
  loadIdentity,
13
14
  preparePairing,
15
+ readPairingClaim,
14
16
  resolveRuntimePaths
15
- } from "../chunk-T35GPRKF.js";
17
+ } from "../chunk-E54NSFGF.js";
16
18
 
17
19
  // src/cli/index.ts
18
20
  import { Command } from "commander";
@@ -281,6 +283,37 @@ async function startDaemonProcess(paths = resolveRuntimePaths()) {
281
283
  }
282
284
  return await getDaemonStatus(paths);
283
285
  }
286
+ async function probeLocalLinkService(options) {
287
+ const unreachable = {
288
+ reachable: false,
289
+ reusable: false,
290
+ linkId: null,
291
+ version: null
292
+ };
293
+ let response;
294
+ try {
295
+ response = await fetch(`http://127.0.0.1:${options.port}/api/v1/bootstrap`, {
296
+ headers: { accept: "application/json" },
297
+ signal: AbortSignal.timeout(options.timeoutMs ?? 1e3)
298
+ });
299
+ } catch {
300
+ return unreachable;
301
+ }
302
+ if (!response.ok) {
303
+ return unreachable;
304
+ }
305
+ const payload = await response.json().catch(() => null);
306
+ if (!payload || payload.api_version !== 1) {
307
+ return unreachable;
308
+ }
309
+ const linkId = typeof payload.link_id === "string" ? payload.link_id : null;
310
+ return {
311
+ reachable: true,
312
+ reusable: options.linkId ? linkId === options.linkId : true,
313
+ linkId,
314
+ version: typeof payload.version === "string" ? payload.version : null
315
+ };
316
+ }
284
317
  async function stopDaemonProcess(paths = resolveRuntimePaths()) {
285
318
  const status = await getDaemonStatus(paths);
286
319
  if (!status.running || !status.pid) {
@@ -572,8 +605,9 @@ var messages = {
572
605
  "pair.code": "Pairing code: {value}",
573
606
  "pair.localApi": "Local API: http://127.0.0.1:{port}",
574
607
  "pair.scan": "Scan this QR code with the HermesPilot App:",
575
- "pair.expires": "Pairing expires in 10 minutes. Press Ctrl+C to stop Hermes Link.",
608
+ "pair.expires": "Pairing expires in 10 minutes. Press Ctrl+C to cancel waiting.",
576
609
  "pair.claimed": "Pairing succeeded. Starting Hermes Link in the background...",
610
+ "pair.claimedRunning": "Pairing succeeded. Hermes Link is already running in the background.",
577
611
  "pair.autostartFailed": "Pairing succeeded, but boot autostart could not be enabled: {message}",
578
612
  "doctor.description": "Run local diagnostics",
579
613
  "doctor.identityOk": "Runtime identity: OK",
@@ -585,7 +619,7 @@ var messages = {
585
619
  "error.relayLinkInvalid": "Relay did not return a valid link_id.",
586
620
  "error.relayEmpty": "Relay returned an empty response.",
587
621
  "error.serverHttp": "HermesPilot Server request failed with HTTP {status}.",
588
- "error.portInUse": "Local port {port} is already in use. Stop the existing Hermes Link process, then run `hermeslink pair` again.",
622
+ "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.",
589
623
  "error.pairingRequires": "Pairing needs HermesPilot Server and Relay, but this command could not start a complete pairing session.",
590
624
  "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."
591
625
  },
@@ -632,8 +666,9 @@ var messages = {
632
666
  "pair.code": "\u914D\u5BF9\u7801\uFF1A{value}",
633
667
  "pair.localApi": "\u672C\u5730 API\uFF1Ahttp://127.0.0.1:{port}",
634
668
  "pair.scan": "\u8BF7\u4F7F\u7528 HermesPilot App \u626B\u63CF\u8FD9\u4E2A\u4E8C\u7EF4\u7801\uFF1A",
635
- "pair.expires": "\u914D\u5BF9\u4F1A\u8BDD 10 \u5206\u949F\u540E\u8FC7\u671F\u3002\u6309 Ctrl+C \u505C\u6B62 Hermes Link\u3002",
669
+ "pair.expires": "\u914D\u5BF9\u4F1A\u8BDD 10 \u5206\u949F\u540E\u8FC7\u671F\u3002\u6309 Ctrl+C \u9000\u51FA\u7B49\u5F85\u3002",
636
670
  "pair.claimed": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002\u6B63\u5728\u628A Hermes Link \u5207\u6362\u5230\u540E\u53F0\u8FD0\u884C...",
671
+ "pair.claimedRunning": "\u914D\u5BF9\u5DF2\u6210\u529F\u3002Hermes Link \u5DF2\u5728\u540E\u53F0\u6301\u7EED\u8FD0\u884C\u3002",
637
672
  "pair.autostartFailed": "\u914D\u5BF9\u5DF2\u6210\u529F\uFF0C\u4F46\u542F\u7528\u5F00\u673A\u81EA\u542F\u5931\u8D25\uFF1A{message}",
638
673
  "doctor.description": "\u8FD0\u884C\u672C\u673A\u8BCA\u65AD",
639
674
  "doctor.identityOk": "\u8FD0\u884C\u8EAB\u4EFD\uFF1A\u6B63\u5E38",
@@ -645,7 +680,7 @@ var messages = {
645
680
  "error.relayLinkInvalid": "Relay \u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684 link_id\u3002",
646
681
  "error.relayEmpty": "Relay \u8FD4\u56DE\u4E86\u7A7A\u54CD\u5E94\u3002",
647
682
  "error.serverHttp": "HermesPilot Server \u8BF7\u6C42\u5931\u8D25\uFF0CHTTP \u72B6\u6001\u7801\uFF1A{status}\u3002",
648
- "error.portInUse": "\u672C\u5730\u7AEF\u53E3 {port} \u5DF2\u88AB\u5360\u7528\u3002\u8BF7\u5148\u505C\u6B62\u5DF2\u6709\u7684 Hermes Link \u8FDB\u7A0B\uFF0C\u7136\u540E\u91CD\u65B0\u8FD0\u884C `hermeslink pair`\u3002",
683
+ "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",
649
684
  "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",
650
685
  "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"
651
686
  }
@@ -809,12 +844,18 @@ program.command("pair").description(helpText("pair.description")).action(async (
809
844
  console.log(t("pair.server", { url: config.serverBaseUrl }));
810
845
  console.log(t("pair.relay", { url: config.relayBaseUrl }));
811
846
  await ensureIdentity(paths);
847
+ const probeBeforePair = await probeLocalLinkService({ port: config.port });
812
848
  const prepared = await preparePairing(paths);
813
- const pairingClaimed = createDeferred();
814
- const service = await startLinkService({
815
- paths,
816
- onPairingClaimed: () => pairingClaimed.resolve()
817
- });
849
+ await clearPairingClaim(prepared.sessionId, paths);
850
+ const probe = await probeLocalLinkService({ port: config.port, linkId: prepared.linkId });
851
+ if (probe.reachable && !probe.reusable) {
852
+ throw new Error(
853
+ `Local Hermes Link on port ${config.port} belongs to ${probe.linkId ?? "an unassigned Link"}, not ${prepared.linkId}.`
854
+ );
855
+ }
856
+ const reusedRunningService = probe.reusable;
857
+ const restartReusedServiceAfterClaim = reusedRunningService && !probeBeforePair.linkId;
858
+ const service = reusedRunningService ? null : await startLinkService({ paths });
818
859
  const qrValue = JSON.stringify(prepared.qrPayload);
819
860
  console.log(t("pair.linkId", { value: prepared.linkId }));
820
861
  console.log(t("pair.code", { value: prepared.code }));
@@ -822,10 +863,13 @@ program.command("pair").description(helpText("pair.description")).action(async (
822
863
  console.log(t("pair.scan"));
823
864
  qrcode.generate(qrValue, { small: true });
824
865
  console.log(t("pair.expires"));
825
- const result = await waitForPairingOrShutdown(pairingClaimed.promise);
826
- await service.close();
866
+ const result = await waitForPairingOrShutdown(prepared.sessionId, paths);
867
+ if (service) {
868
+ await service.close();
869
+ }
827
870
  if (result === "claimed") {
828
- console.log(t("pair.claimed"));
871
+ await clearPairingClaim(prepared.sessionId, paths);
872
+ console.log(t(reusedRunningService ? "pair.claimedRunning" : "pair.claimed"));
829
873
  try {
830
874
  const autostart2 = await enableAutostart();
831
875
  if (autostart2.supported && autostart2.enabled) {
@@ -835,8 +879,14 @@ program.command("pair").description(helpText("pair.description")).action(async (
835
879
  const message = error instanceof Error ? error.message : String(error);
836
880
  console.log(t("pair.autostartFailed", { message }));
837
881
  }
838
- const status = await startDaemonProcess(paths);
839
- console.log(t("start.backgroundStarted", { pid: status.pid ?? "unknown" }));
882
+ if (restartReusedServiceAfterClaim) {
883
+ await stopDaemonProcess(paths);
884
+ const status = await startDaemonProcess(paths);
885
+ console.log(t("start.backgroundStarted", { pid: status.pid ?? "unknown" }));
886
+ } else if (!reusedRunningService) {
887
+ const status = await startDaemonProcess(paths);
888
+ console.log(t("start.backgroundStarted", { pid: status.pid ?? "unknown" }));
889
+ }
840
890
  }
841
891
  });
842
892
  var autostart = program.command("autostart").description(helpText("autostart.description"));
@@ -914,25 +964,28 @@ async function waitForShutdown(cleanup) {
914
964
  });
915
965
  await cleanup();
916
966
  }
917
- async function waitForPairingOrShutdown(pairingClaimed) {
918
- let stop = null;
919
- const shutdown = new Promise((resolve) => {
920
- stop = () => resolve("shutdown");
921
- process.once("SIGINT", stop);
922
- process.once("SIGTERM", stop);
923
- });
924
- const result = await Promise.race([pairingClaimed.then(() => "claimed"), shutdown]);
925
- if (stop) {
967
+ async function waitForPairingOrShutdown(sessionId, paths) {
968
+ let shutdownRequested = false;
969
+ const stop = () => {
970
+ shutdownRequested = true;
971
+ };
972
+ process.once("SIGINT", stop);
973
+ process.once("SIGTERM", stop);
974
+ try {
975
+ while (!shutdownRequested) {
976
+ const record = await readPairingClaim(sessionId, paths);
977
+ if (record) {
978
+ return "claimed";
979
+ }
980
+ await sleep(250);
981
+ }
982
+ return "shutdown";
983
+ } finally {
926
984
  process.off("SIGINT", stop);
927
985
  process.off("SIGTERM", stop);
928
986
  }
929
- return result;
930
987
  }
931
- function createDeferred() {
932
- let resolve;
933
- const promise = new Promise((innerResolve) => {
934
- resolve = innerResolve;
935
- });
936
- return { promise, resolve };
988
+ function sleep(ms) {
989
+ return new Promise((resolve) => setTimeout(resolve, ms));
937
990
  }
938
991
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/autostart/autostart.ts","../../src/daemon/process.ts","../../src/daemon/service.ts","../../src/relay/control-client.ts","../../src/i18n.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport qrcode from 'qrcode-terminal'\nimport { LINK_COMMAND, LINK_VERSION } from '../constants.js'\nimport { enableAutostart, disableAutostart, getAutostartStatus } from '../autostart/autostart.js'\nimport { loadConfig } from '../config/config.js'\nimport { getDaemonStatus, startDaemonProcess, stopDaemonProcess, daemonLogFile } from '../daemon/process.js'\nimport { startLinkService } from '../daemon/service.js'\nimport { ensureHermesApiServerKey } from '../hermes/config.js'\nimport { ensureIdentity, getIdentityStatus, loadIdentity } from '../identity/identity.js'\nimport { detectSystemLanguage, localizeErrorMessage, resolveLanguage, translate } from '../i18n.js'\nimport { preparePairing } from '../pairing/pairing.js'\nimport { getLinkLogFile } from '../runtime/logger.js'\nimport { resolveRuntimePaths } from '../runtime/paths.js'\n\nconst program = new Command()\nconst helpLanguage = detectSystemLanguage()\nconst helpText = translate.bind(null, helpLanguage)\n\nprogram\n .name(LINK_COMMAND)\n .description(helpText('program.description'))\n .version(LINK_VERSION, '-v, --version', helpText('program.version'))\n\nprogram\n .command('status')\n .option('--json', helpText('status.json'))\n .description(helpText('status.description'))\n .action(async (options: { json?: boolean }) => {\n const paths = resolveRuntimePaths()\n const [identity, config] = await Promise.all([loadIdentity(paths), loadConfig(paths)])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const payload = {\n version: LINK_VERSION,\n runtimeHome: paths.homeDir,\n paired: Boolean(identity?.link_id),\n mode: identity?.link_id ? 'paired' : 'local-only',\n port: config.port,\n identity: identity ? getIdentityStatus(identity) : null,\n relay: {\n configured: Boolean(config.relayBaseUrl),\n connected: false,\n },\n }\n if (options.json) {\n console.log(JSON.stringify(payload, null, 2))\n return\n }\n console.log(`Hermes Link ${payload.version}`)\n console.log(t('status.runtime', { value: payload.runtimeHome }))\n console.log(t('status.mode', { value: payload.mode }))\n console.log(t('status.port', { value: payload.port }))\n console.log(t('status.linkId', { value: payload.identity?.linkId ?? t('status.notPaired') }))\n })\n\nprogram\n .command('start')\n .description(helpText('start.description'))\n .action(async () => {\n const [config, status] = await Promise.all([loadConfig(), getDaemonStatus()])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n if (status.running && status.pid) {\n console.log(t('start.alreadyRunning', { pid: status.pid }))\n return\n }\n const nextStatus = await startDaemonProcess()\n console.log(t('start.backgroundStarted', { pid: nextStatus.pid ?? 'unknown' }))\n })\n\nprogram\n .command('stop')\n .description(helpText('stop.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const before = await getDaemonStatus()\n if (!before.running) {\n console.log(t('stop.notRunning'))\n return\n }\n await stopDaemonProcess()\n console.log(t('stop.stopped'))\n })\n\nprogram\n .command('restart')\n .description(helpText('restart.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n await stopDaemonProcess()\n const status = await startDaemonProcess()\n console.log(t('start.backgroundStarted', { pid: status.pid ?? 'unknown' }))\n })\n\nprogram\n .command('daemon')\n .option('--foreground', 'run in foreground')\n .description(helpText('daemon.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const service = await startLinkService({ writePidFile: true })\n console.log(t('daemon.foreground'))\n await waitForShutdown(async () => {\n await service.close()\n })\n })\n\nprogram\n .command('pair')\n .description(helpText('pair.description'))\n .action(async () => {\n const paths = resolveRuntimePaths()\n const config = await loadConfig(paths)\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n console.log(t('pair.preparing'))\n console.log(t('pair.server', { url: config.serverBaseUrl }))\n console.log(t('pair.relay', { url: config.relayBaseUrl }))\n await ensureIdentity(paths)\n const prepared = await preparePairing(paths)\n const pairingClaimed = createDeferred()\n const service = await startLinkService({\n paths,\n onPairingClaimed: () => pairingClaimed.resolve(),\n })\n const qrValue = JSON.stringify(prepared.qrPayload)\n console.log(t('pair.linkId', { value: prepared.linkId }))\n console.log(t('pair.code', { value: prepared.code }))\n console.log(t('pair.localApi', { port: config.port }))\n console.log(t('pair.scan'))\n qrcode.generate(qrValue, { small: true })\n console.log(t('pair.expires'))\n const result = await waitForPairingOrShutdown(pairingClaimed.promise)\n await service.close()\n if (result === 'claimed') {\n console.log(t('pair.claimed'))\n try {\n const autostart = await enableAutostart()\n if (autostart.supported && autostart.enabled) {\n console.log(t('autostart.enabled', { method: autostart.method, path: autostart.filePath ?? '' }))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.log(t('pair.autostartFailed', { message }))\n }\n const status = await startDaemonProcess(paths)\n console.log(t('start.backgroundStarted', { pid: status.pid ?? 'unknown' }))\n }\n })\n\nconst autostart = program.command('autostart').description(helpText('autostart.description'))\n\nautostart\n .command('on')\n .description(helpText('autostart.on.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const status = await enableAutostart()\n if (!status.supported) {\n console.log(t('autostart.unsupported'))\n return\n }\n console.log(t('autostart.enabled', { method: status.method, path: status.filePath ?? '' }))\n })\n\nautostart\n .command('off')\n .description(helpText('autostart.off.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n await disableAutostart()\n console.log(t('autostart.disabled'))\n })\n\nautostart\n .command('status')\n .description(helpText('autostart.status.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const status = await getAutostartStatus()\n if (!status.supported) {\n console.log(t('autostart.unsupported'))\n return\n }\n console.log(\n t(status.enabled ? 'autostart.status.enabled' : 'autostart.status.disabled', {\n method: status.method,\n path: status.filePath ?? '',\n }),\n )\n })\n\nprogram\n .command('logs')\n .description(helpText('logs.description'))\n .action(async () => {\n const paths = resolveRuntimePaths()\n const config = await loadConfig(paths)\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n console.log(t('logs.servicePath', { path: getLinkLogFile(paths) }))\n console.log(t('logs.daemonPath', { path: daemonLogFile(paths) }))\n })\n\nprogram\n .command('doctor')\n .description(helpText('doctor.description'))\n .action(async () => {\n const [identity, config] = await Promise.all([ensureIdentity(), loadConfig()])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const hermesConfig = await ensureHermesApiServerKey()\n console.log(t('doctor.identityOk'))\n console.log(t('doctor.installId', { value: identity.install_id }))\n console.log(t('doctor.linkId', { value: identity.link_id ?? t('doctor.notAssigned') }))\n if (hermesConfig.notice) {\n console.log(hermesConfig.notice)\n if (hermesConfig.backupPath) {\n console.log(`Hermes config backup: ${hermesConfig.backupPath}`)\n }\n }\n })\n\nprogram.parseAsync(process.argv).catch(async (error) => {\n const language = await loadCliLanguage().catch(() => detectSystemLanguage())\n console.error(localizeErrorMessage(error, language))\n process.exitCode = 1\n})\n\nasync function loadCliLanguage() {\n const config = await loadConfig()\n return resolveLanguage(config.language)\n}\n\nasync function waitForShutdown(cleanup: () => Promise<void>): Promise<void> {\n await new Promise<void>((resolve) => {\n const stop = () => resolve()\n process.once('SIGINT', stop)\n process.once('SIGTERM', stop)\n })\n await cleanup()\n}\n\nasync function waitForPairingOrShutdown(pairingClaimed: Promise<void>): Promise<'claimed' | 'shutdown'> {\n let stop: (() => void) | null = null\n const shutdown = new Promise<'shutdown'>((resolve) => {\n stop = () => resolve('shutdown')\n process.once('SIGINT', stop)\n process.once('SIGTERM', stop)\n })\n const result = await Promise.race([pairingClaimed.then(() => 'claimed' as const), shutdown])\n if (stop) {\n process.off('SIGINT', stop)\n process.off('SIGTERM', stop)\n }\n return result\n}\n\nfunction createDeferred(): { promise: Promise<void>; resolve: () => void } {\n let resolve!: () => void\n const promise = new Promise<void>((innerResolve) => {\n resolve = innerResolve\n })\n return { promise, resolve }\n}\n","import { execFile } from 'node:child_process'\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { promisify } from 'node:util'\nimport { currentCliScriptPath } from '../daemon/process.js'\n\nconst execFileAsync = promisify(execFile)\nconst MACOS_LABEL = 'com.hermespilot.link'\n\nexport interface AutostartStatus {\n supported: boolean\n enabled: boolean\n method: 'launchd' | 'systemd-user' | 'xdg-autostart' | 'windows-startup' | 'unsupported'\n filePath: string | null\n}\n\nexport async function enableAutostart(): Promise<AutostartStatus> {\n const definition = await resolveAutostartDefinition()\n if (!definition) {\n return unsupportedStatus()\n }\n await mkdir(path.dirname(definition.filePath), { recursive: true, mode: 0o700 })\n await writeFile(definition.filePath, definition.content, { mode: 0o600 })\n if (definition.method === 'systemd-user') {\n await execFileAsync('systemctl', ['--user', 'enable', path.basename(definition.filePath)]).catch(async () => {\n await rm(definition.filePath, { force: true }).catch(() => undefined)\n const fallback = xdgAutostartDefinition()\n await mkdir(path.dirname(fallback.filePath), { recursive: true, mode: 0o700 })\n await writeFile(fallback.filePath, fallback.content, { mode: 0o600 })\n })\n }\n return await getAutostartStatus()\n}\n\nexport async function disableAutostart(): Promise<AutostartStatus> {\n const definitions = await allAutostartDefinitions()\n for (const definition of definitions) {\n if (definition.method === 'systemd-user') {\n await execFileAsync('systemctl', ['--user', 'disable', path.basename(definition.filePath)]).catch(() => undefined)\n }\n await rm(definition.filePath, { force: true }).catch(() => undefined)\n }\n return await getAutostartStatus()\n}\n\nexport async function getAutostartStatus(): Promise<AutostartStatus> {\n const definitions = await allAutostartDefinitions()\n if (definitions.length === 0) {\n return unsupportedStatus()\n }\n for (const definition of definitions) {\n const content = await readFile(definition.filePath, 'utf8').catch(() => null)\n if (content !== null) {\n return {\n supported: true,\n enabled: true,\n method: definition.method,\n filePath: definition.filePath,\n }\n }\n }\n const primary = definitions[0]\n return {\n supported: true,\n enabled: false,\n method: primary.method,\n filePath: primary.filePath,\n }\n}\n\nasync function resolveAutostartDefinition(): Promise<AutostartDefinition | null> {\n if (process.platform === 'darwin') {\n return launchdDefinition()\n }\n if (process.platform === 'win32') {\n return windowsStartupDefinition()\n }\n if (process.platform === 'linux') {\n return await hasSystemctlUser() ? systemdUserDefinition() : xdgAutostartDefinition()\n }\n return null\n}\n\nasync function allAutostartDefinitions(): Promise<AutostartDefinition[]> {\n if (process.platform === 'darwin') {\n return [launchdDefinition()]\n }\n if (process.platform === 'win32') {\n return [windowsStartupDefinition()]\n }\n if (process.platform === 'linux') {\n return [systemdUserDefinition(), xdgAutostartDefinition()]\n }\n return []\n}\n\nasync function hasSystemctlUser(): Promise<boolean> {\n try {\n await execFileAsync('systemctl', ['--user', 'show-environment'])\n return true\n } catch {\n return false\n }\n}\n\nfunction launchdDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${MACOS_LABEL}.plist`)\n return {\n method: 'launchd',\n filePath,\n content: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${MACOS_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${xmlEscape(process.execPath)}</string>\n <string>${xmlEscape(currentCliScriptPath())}</string>\n <string>daemon</string>\n <string>--foreground</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <false/>\n <key>StandardOutPath</key>\n <string>${xmlEscape(path.join(os.homedir(), '.hermeslink', 'logs', 'daemon.log'))}</string>\n <key>StandardErrorPath</key>\n <string>${xmlEscape(path.join(os.homedir(), '.hermeslink', 'logs', 'daemon.log'))}</string>\n</dict>\n</plist>\n`,\n }\n}\n\nfunction systemdUserDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), '.config', 'systemd', 'user', 'hermeslink.service')\n return {\n method: 'systemd-user',\n filePath,\n content: `[Unit]\nDescription=Hermes Link\nAfter=network-online.target\n\n[Service]\nType=simple\nExecStart=${systemdQuote(process.execPath)} ${systemdQuote(currentCliScriptPath())} daemon --foreground\nRestart=no\n\n[Install]\nWantedBy=default.target\n`,\n }\n}\n\nfunction xdgAutostartDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), '.config', 'autostart', 'hermeslink.desktop')\n return {\n method: 'xdg-autostart',\n filePath,\n content: `[Desktop Entry]\nType=Application\nName=Hermes Link\nExec=${desktopQuote(process.execPath)} ${desktopQuote(currentCliScriptPath())} daemon --foreground\nTerminal=false\nX-GNOME-Autostart-enabled=true\n`,\n }\n}\n\nfunction windowsStartupDefinition(): AutostartDefinition {\n const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming')\n const filePath = path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'HermesLink.cmd')\n return {\n method: 'windows-startup',\n filePath,\n content: `@echo off\\r\\nstart \"\" /min \"${process.execPath}\" \"${currentCliScriptPath()}\" daemon --foreground\\r\\n`,\n }\n}\n\nfunction unsupportedStatus(): AutostartStatus {\n return {\n supported: false,\n enabled: false,\n method: 'unsupported',\n filePath: null,\n }\n}\n\nfunction xmlEscape(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;')\n}\n\nfunction systemdQuote(value: string): string {\n return `\"${value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`\n}\n\nfunction desktopQuote(value: string): string {\n return `\"${value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`\n}\n\ninterface AutostartDefinition {\n method: AutostartStatus['method']\n filePath: string\n content: string\n}\n","import { spawn } from 'node:child_process'\nimport { mkdir, open, readFile, rm } from 'node:fs/promises'\nimport path from 'node:path'\nimport { pidFilePath } from './service.js'\nimport { resolveRuntimePaths, type RuntimePaths } from '../runtime/paths.js'\n\nexport interface DaemonStatus {\n running: boolean\n pid: number | null\n pidFile: string\n logFile: string\n}\n\nexport async function startDaemonProcess(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const status = await getDaemonStatus(paths)\n if (status.running) {\n return status\n }\n await mkdir(paths.logsDir, { recursive: true, mode: 0o700 })\n await mkdir(paths.runDir, { recursive: true, mode: 0o700 })\n const log = await open(daemonLogFile(paths), 'a', 0o600)\n const scriptPath = currentCliScriptPath()\n const child = spawn(process.execPath, [scriptPath, 'daemon', '--foreground'], {\n detached: true,\n stdio: ['ignore', log.fd, log.fd],\n env: process.env,\n })\n child.unref()\n await log.close()\n for (let index = 0; index < 12; index += 1) {\n await wait(250)\n const next = await getDaemonStatus(paths)\n if (next.running) {\n return next\n }\n }\n return await getDaemonStatus(paths)\n}\n\nexport async function stopDaemonProcess(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const status = await getDaemonStatus(paths)\n if (!status.running || !status.pid) {\n return status\n }\n try {\n process.kill(status.pid, 'SIGTERM')\n } catch {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n return await getDaemonStatus(paths)\n }\n for (let index = 0; index < 20; index += 1) {\n await wait(250)\n if (!isProcessAlive(status.pid)) {\n break\n }\n }\n if (!isProcessAlive(status.pid)) {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n }\n return await getDaemonStatus(paths)\n}\n\nexport async function getDaemonStatus(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const pidFile = pidFilePath(paths)\n const pid = await readPid(pidFile)\n if (pid && !isProcessAlive(pid)) {\n await rm(pidFile, { force: true }).catch(() => undefined)\n return {\n running: false,\n pid: null,\n pidFile,\n logFile: daemonLogFile(paths),\n }\n }\n return {\n running: Boolean(pid),\n pid,\n pidFile,\n logFile: daemonLogFile(paths),\n }\n}\n\nexport function daemonLogFile(paths: RuntimePaths = resolveRuntimePaths()): string {\n return path.join(paths.logsDir, 'daemon.log')\n}\n\nexport function currentCliScriptPath(): string {\n return process.argv[1]\n}\n\nasync function readPid(filePath: string): Promise<number | null> {\n const raw = await readFile(filePath, 'utf8').catch(() => null)\n if (!raw) {\n return null\n }\n const pid = Number.parseInt(raw.trim(), 10)\n return Number.isInteger(pid) && pid > 0 ? pid : null\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","import { createServer, type Server } from 'node:http'\nimport { mkdir, rm, writeFile } from 'node:fs/promises'\nimport { createApp, type CreateAppOptions } from '../http/app.js'\nimport { loadConfig } from '../config/config.js'\nimport { loadIdentity } from '../identity/identity.js'\nimport { connectRelayControl, type RelayControlClient } from '../relay/control-client.js'\nimport { createFileLogger } from '../runtime/logger.js'\nimport { resolveRuntimePaths, type RuntimePaths } from '../runtime/paths.js'\n\nexport interface LinkService {\n close(): Promise<void>\n}\n\nexport interface StartLinkServiceOptions extends CreateAppOptions {\n paths?: RuntimePaths\n writePidFile?: boolean\n relayMaxReconnectAttempts?: number\n}\n\nexport async function startLinkService(options: StartLinkServiceOptions = {}): Promise<LinkService> {\n const paths = options.paths ?? resolveRuntimePaths()\n const logger = createFileLogger({ paths })\n const [identity, config] = await Promise.all([loadIdentity(paths), loadConfig(paths)])\n await logger.info('service_starting', {\n port: config.port,\n mode: identity?.link_id ? 'paired' : 'local-only',\n })\n const app = await createApp({ paths, logger, onPairingClaimed: options.onPairingClaimed })\n const server = createServer(app.callback())\n try {\n await listenServer(server, config.port)\n } catch (error) {\n await logger.error('service_start_failed', {\n port: config.port,\n error: error instanceof Error ? error.message : String(error),\n })\n await logger.flush()\n throw error\n }\n server.on('error', (error) => {\n void logger.error('service_error', { error: error.message })\n })\n void logger.info('service_started', {\n port: config.port,\n link_id: identity?.link_id ?? null,\n })\n let relay: RelayControlClient | null = null\n if (identity?.link_id) {\n relay = connectRelayControl({\n relayBaseUrl: config.relayBaseUrl,\n linkId: identity.link_id,\n localPort: config.port,\n maxReconnectAttempts: options.relayMaxReconnectAttempts ?? 5,\n backoffBaseMs: 1_000,\n backoffMaxMs: 30_000,\n onStatus: (status) => {\n void logger.info('relay_status', status)\n },\n })\n } else {\n void logger.info('relay_skipped', { reason: 'link_not_paired' })\n }\n if (options.writePidFile) {\n await writePidFile(paths)\n }\n return {\n async close() {\n relay?.close()\n await closeServer(server)\n await logger.info('service_stopped')\n await logger.flush()\n if (options.writePidFile) {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n }\n },\n }\n}\n\nexport function pidFilePath(paths: RuntimePaths = resolveRuntimePaths()): string {\n return `${paths.runDir}/hermeslink.pid`\n}\n\nasync function writePidFile(paths: RuntimePaths): Promise<void> {\n await mkdir(paths.runDir, { recursive: true, mode: 0o700 })\n await writeFile(pidFilePath(paths), `${process.pid}\\n`, { mode: 0o600 })\n}\n\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error)\n return\n }\n resolve()\n })\n })\n}\n\nasync function listenServer(server: Server, port: number): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n server.off('error', onError)\n server.off('listening', onListening)\n }\n const onError = (error: Error) => {\n cleanup()\n reject(error)\n }\n const onListening = () => {\n cleanup()\n resolve()\n }\n\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(port)\n })\n}\n","import WebSocket from 'ws'\nimport { LINK_VERSION } from '../constants.js'\n\ninterface RelayRequestFrame {\n type: 'http.request'\n id: string\n method: string\n path: string\n headers?: Record<string, string>\n bodyBase64?: string | null\n}\n\ninterface RelayCancelFrame {\n type: 'http.cancel'\n id: string\n}\n\ntype RelayFrame = RelayRequestFrame | RelayCancelFrame\n\nexport interface RelayControlClient {\n close(): void\n}\n\nexport function connectRelayControl(options: {\n relayBaseUrl: string\n linkId: string\n localPort: number\n maxReconnectAttempts?: number\n backoffBaseMs?: number\n backoffMaxMs?: number\n onStatus?: (status: { state: 'connecting' | 'connected' | 'disconnected' | 'retrying' | 'failed'; attempt: number; message?: string }) => void\n}): RelayControlClient {\n const wsUrl = new URL(`${options.relayBaseUrl.replace(/\\/+$/u, '')}/api/v1/relay/link/connect`)\n wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:'\n wsUrl.searchParams.set('link_id', options.linkId)\n\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 5\n const backoffBaseMs = options.backoffBaseMs ?? 1_000\n const backoffMaxMs = options.backoffMaxMs ?? 30_000\n let reconnectAttempts = 0\n let closedByUser = false\n let socket: WebSocket | null = null\n let retryTimer: ReturnType<typeof setTimeout> | null = null\n let abortControllers = new Map<string, AbortController>()\n\n const connect = () => {\n options.onStatus?.({ state: 'connecting', attempt: reconnectAttempts })\n socket = new WebSocket(wsUrl, {\n headers: {\n 'x-hermes-link-version': LINK_VERSION,\n },\n })\n socket.on('open', () => {\n reconnectAttempts = 0\n options.onStatus?.({ state: 'connected', attempt: reconnectAttempts })\n })\n socket.on('message', (raw) => {\n if (!socket || (typeof raw !== 'string' && !Buffer.isBuffer(raw))) {\n return\n }\n void handleFrame(socket, String(raw), options.localPort, abortControllers).catch((error) => {\n const message = error instanceof Error ? error.message : 'Relay request failed'\n socket?.send(JSON.stringify({ type: 'http.error', id: 'unknown', status: 502, message }))\n })\n })\n socket.on('error', (error) => {\n const message = error instanceof Error ? error.message : 'Relay websocket error'\n options.onStatus?.({ state: 'disconnected', attempt: reconnectAttempts, message })\n })\n socket.on('close', () => {\n abortAll(abortControllers)\n abortControllers = new Map<string, AbortController>()\n if (closedByUser) {\n options.onStatus?.({ state: 'disconnected', attempt: reconnectAttempts })\n return\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n options.onStatus?.({ state: 'failed', attempt: reconnectAttempts, message: 'Relay reconnect attempts exhausted' })\n return\n }\n reconnectAttempts += 1\n const delay = computeBackoffMs(reconnectAttempts, backoffBaseMs, backoffMaxMs)\n options.onStatus?.({ state: 'retrying', attempt: reconnectAttempts, message: `Retrying in ${delay}ms` })\n retryTimer = setTimeout(connect, delay)\n retryTimer.unref?.()\n })\n }\n\n connect()\n\n return {\n close() {\n closedByUser = true\n if (retryTimer) {\n clearTimeout(retryTimer)\n }\n abortAll(abortControllers)\n socket?.close()\n },\n }\n}\n\nfunction abortAll(abortControllers: Map<string, AbortController>): void {\n for (const controller of abortControllers.values()) {\n controller.abort()\n }\n abortControllers.clear()\n}\n\nfunction computeBackoffMs(attempt: number, baseMs: number, maxMs: number): number {\n const exponential = Math.min(maxMs, baseMs * 2 ** Math.max(0, attempt - 1))\n const jitter = Math.floor(Math.random() * Math.min(1_000, exponential * 0.2))\n return exponential + jitter\n}\n\nasync function handleFrame(\n socket: WebSocket,\n raw: string,\n localPort: number,\n abortControllers: Map<string, AbortController>,\n): Promise<void> {\n const frame = JSON.parse(raw) as RelayFrame\n if (frame.type === 'http.cancel') {\n abortControllers.get(frame.id)?.abort()\n abortControllers.delete(frame.id)\n return\n }\n if (frame.type !== 'http.request') {\n return\n }\n const abortController = new AbortController()\n abortControllers.set(frame.id, abortController)\n try {\n const response = await fetch(`http://127.0.0.1:${localPort}${frame.path}`, {\n method: frame.method,\n headers: frame.headers ?? {},\n body: frame.bodyBase64 ? Buffer.from(frame.bodyBase64, 'base64') : undefined,\n signal: abortController.signal,\n })\n const headers = Object.fromEntries(response.headers.entries())\n const contentType = response.headers.get('content-type') ?? ''\n if (response.body && contentType.includes('text/event-stream')) {\n socket.send(JSON.stringify({ type: 'http.stream.start', id: frame.id, status: response.status, headers }))\n const reader = response.body.getReader()\n while (true) {\n const next = await reader.read()\n if (next.done) {\n break\n }\n socket.send(JSON.stringify({ type: 'http.stream.chunk', id: frame.id, bodyBase64: Buffer.from(next.value).toString('base64') }))\n }\n socket.send(JSON.stringify({ type: 'http.stream.end', id: frame.id }))\n return\n }\n const body = Buffer.from(await response.arrayBuffer()).toString('base64')\n socket.send(JSON.stringify({ type: 'http.response', id: frame.id, status: response.status, headers, bodyBase64: body }))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Relay request failed'\n socket.send(JSON.stringify({ type: 'http.error', id: frame.id, status: 502, message }))\n } finally {\n abortControllers.delete(frame.id)\n }\n}\n","export type SupportedLanguage = 'zh-CN' | 'en'\nexport type ConfiguredLanguage = SupportedLanguage | 'auto'\n\nconst messages = {\n en: {\n 'program.description': 'Hermes Link companion service',\n 'program.version': 'print Hermes Link version',\n 'status.description': 'Show local Hermes Link status',\n 'status.json': 'print machine-readable status',\n 'status.runtime': 'Runtime: {value}',\n 'status.mode': 'Mode: {value}',\n 'status.port': 'Local port: {value}',\n 'status.linkId': 'Link ID: {value}',\n 'status.notPaired': 'not paired',\n 'start.description': 'Start Hermes Link daemon',\n 'start.backgroundStarted': 'Hermes Link is running in the background. PID: {pid}',\n 'start.alreadyRunning': 'Hermes Link is already running. PID: {pid}',\n 'start.notPaired': 'Hermes Link is not paired yet. Starting in local-only maintenance mode.',\n 'start.notPaired.detail':\n 'Relay, Server polling, and LAN entrypoints stay disabled until you run `hermeslink pair`.',\n 'start.listening': 'Hermes Link API listening on http://127.0.0.1:{port}',\n 'start.relayConnecting': 'Relay control connecting for {linkId}',\n 'stop.description': 'Stop the background Hermes Link daemon',\n 'stop.stopped': 'Hermes Link stopped.',\n 'stop.notRunning': 'Hermes Link is not running.',\n 'restart.description': 'Restart the background Hermes Link daemon',\n 'daemon.description': 'Run Hermes Link in the foreground',\n 'daemon.foreground': 'Hermes Link foreground daemon is running. Press Ctrl+C to stop.',\n 'logs.description': 'Show Hermes Link log paths',\n 'logs.servicePath': 'Service log: {path}',\n 'logs.daemonPath': 'Daemon stdout/stderr log: {path}',\n 'autostart.description': 'Manage boot autostart',\n 'autostart.on.description': 'Enable boot autostart',\n 'autostart.off.description': 'Disable boot autostart',\n 'autostart.status.description': 'Show boot autostart status',\n 'autostart.enabled': 'Boot autostart enabled via {method}: {path}',\n 'autostart.disabled': 'Boot autostart disabled.',\n 'autostart.status.enabled': 'Boot autostart: enabled via {method}: {path}',\n 'autostart.status.disabled': 'Boot autostart: disabled. Method: {method}. File: {path}',\n 'autostart.unsupported': 'Boot autostart is not supported on this platform yet.',\n 'pair.description': 'Create a Hermes Link pairing session',\n 'pair.preparing': 'Preparing pairing session through HermesPilot Server and Relay...',\n 'pair.server': 'Server: {url}',\n 'pair.relay': 'Relay: {url}',\n 'pair.linkId': 'Hermes Link ID: {value}',\n 'pair.code': 'Pairing code: {value}',\n 'pair.localApi': 'Local API: http://127.0.0.1:{port}',\n 'pair.scan': 'Scan this QR code with the HermesPilot App:',\n 'pair.expires': 'Pairing expires in 10 minutes. Press Ctrl+C to stop Hermes Link.',\n 'pair.claimed': 'Pairing succeeded. Starting Hermes Link in the background...',\n 'pair.autostartFailed': 'Pairing succeeded, but boot autostart could not be enabled: {message}',\n 'doctor.description': 'Run local diagnostics',\n 'doctor.identityOk': 'Runtime identity: OK',\n 'doctor.installId': 'Install ID: {value}',\n 'doctor.linkId': 'Link ID: {value}',\n 'doctor.notAssigned': 'not assigned',\n 'error.relayPublicKeyMismatch':\n 'Relay rejected the pairing request because the Server-issued bootstrap token does not match this Link public key. Make sure Server and Relay are deployed with the same bootstrap key configuration, then run `hermeslink pair` again.',\n 'error.relayChallengeInvalid': 'Relay did not return a valid install challenge.',\n 'error.relayLinkInvalid': 'Relay did not return a valid link_id.',\n 'error.relayEmpty': 'Relay returned an empty response.',\n 'error.serverHttp': 'HermesPilot Server request failed with HTTP {status}.',\n 'error.portInUse':\n 'Local port {port} is already in use. Stop the existing Hermes Link process, then run `hermeslink pair` again.',\n 'error.pairingRequires':\n 'Pairing needs HermesPilot Server and Relay, but this command could not start a complete pairing session.',\n 'error.pairingRequires.detail':\n '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.',\n },\n 'zh-CN': {\n 'program.description': 'Hermes Link 本地伴随服务',\n 'program.version': '输出 Hermes Link 版本号',\n 'status.description': '查看本机 Hermes Link 状态',\n 'status.json': '输出机器可读的状态 JSON',\n 'status.runtime': '运行目录:{value}',\n 'status.mode': '模式:{value}',\n 'status.port': '本地端口:{value}',\n 'status.linkId': 'Link ID:{value}',\n 'status.notPaired': '尚未配对',\n 'start.description': '启动 Hermes Link 服务',\n 'start.backgroundStarted': 'Hermes Link 已在后台运行。PID:{pid}',\n 'start.alreadyRunning': 'Hermes Link 已经在运行。PID:{pid}',\n 'start.notPaired': 'Hermes Link 还没有配对,将以本地维护模式启动。',\n 'start.notPaired.detail': '在你运行 `hermeslink pair` 前,Relay、Server 轮询和局域网入口都会保持关闭。',\n 'start.listening': 'Hermes Link API 正在监听 http://127.0.0.1:{port}',\n 'start.relayConnecting': '正在为 {linkId} 连接 Relay 控制通道',\n 'stop.description': '停止后台 Hermes Link 服务',\n 'stop.stopped': 'Hermes Link 已停止。',\n 'stop.notRunning': 'Hermes Link 没有在运行。',\n 'restart.description': '重启后台 Hermes Link 服务',\n 'daemon.description': '以前台方式运行 Hermes Link',\n 'daemon.foreground': 'Hermes Link 前台服务正在运行。按 Ctrl+C 停止。',\n 'logs.description': '显示 Hermes Link 日志路径',\n 'logs.servicePath': '服务日志:{path}',\n 'logs.daemonPath': 'Daemon 标准输出/错误日志:{path}',\n 'autostart.description': '管理开机自启',\n 'autostart.on.description': '启用开机自启',\n 'autostart.off.description': '关闭开机自启',\n 'autostart.status.description': '查看开机自启状态',\n 'autostart.enabled': '已启用开机自启,方式:{method},文件:{path}',\n 'autostart.disabled': '已关闭开机自启。',\n 'autostart.status.enabled': '开机自启:已启用,方式:{method},文件:{path}',\n 'autostart.status.disabled': '开机自启:未启用。方式:{method},文件:{path}',\n 'autostart.unsupported': '当前平台暂不支持开机自启。',\n 'pair.description': '创建 Hermes Link 配对会话',\n 'pair.preparing': '正在通过 HermesPilot Server 和 Relay 创建配对会话...',\n 'pair.server': 'Server:{url}',\n 'pair.relay': 'Relay:{url}',\n 'pair.linkId': 'Hermes Link ID:{value}',\n 'pair.code': '配对码:{value}',\n 'pair.localApi': '本地 API:http://127.0.0.1:{port}',\n 'pair.scan': '请使用 HermesPilot App 扫描这个二维码:',\n 'pair.expires': '配对会话 10 分钟后过期。按 Ctrl+C 停止 Hermes Link。',\n 'pair.claimed': '配对已成功。正在把 Hermes Link 切换到后台运行...',\n 'pair.autostartFailed': '配对已成功,但启用开机自启失败:{message}',\n 'doctor.description': '运行本机诊断',\n 'doctor.identityOk': '运行身份:正常',\n 'doctor.installId': 'Install ID:{value}',\n 'doctor.linkId': 'Link ID:{value}',\n 'doctor.notAssigned': '尚未分配',\n 'error.relayPublicKeyMismatch':\n 'Relay 拒绝了配对请求:Server 签发的 bootstrap token 与本机 Link 公钥不匹配。请确认 Server 和 Relay 使用同一套 bootstrap key 配置,然后重新运行 `hermeslink pair`。',\n 'error.relayChallengeInvalid': 'Relay 没有返回有效的安装挑战。',\n 'error.relayLinkInvalid': 'Relay 没有返回有效的 link_id。',\n 'error.relayEmpty': 'Relay 返回了空响应。',\n 'error.serverHttp': 'HermesPilot Server 请求失败,HTTP 状态码:{status}。',\n 'error.portInUse': '本地端口 {port} 已被占用。请先停止已有的 Hermes Link 进程,然后重新运行 `hermeslink pair`。',\n 'error.pairingRequires': '配对需要 HermesPilot Server 和 Relay,但当前命令没有能启动完整配对会话。',\n 'error.pairingRequires.detail':\n '云端服务可以是已部署且健康的;本机 Link 仍必须先向 Server 申请短期 relay bootstrap token,才能再向 Relay 申请 link_id。',\n },\n} satisfies Record<SupportedLanguage, Record<string, string>>\n\nexport type MessageKey = keyof (typeof messages)['en']\n\nexport function detectSystemLanguage(env: NodeJS.ProcessEnv = process.env): SupportedLanguage {\n const candidates = [\n env.HERMESLINK_LANG,\n env.HERMESLINK_LANGUAGE,\n env.LC_ALL,\n env.LC_MESSAGES,\n env.LANG,\n env.LANGUAGE?.split(':')[0],\n Intl.DateTimeFormat().resolvedOptions().locale,\n ]\n for (const candidate of candidates) {\n const language = parseLanguage(candidate)\n if (language) {\n return language\n }\n }\n return 'en'\n}\n\nexport function resolveLanguage(setting?: ConfiguredLanguage | string | null): SupportedLanguage {\n const configured = parseLanguage(setting)\n if (configured) {\n return configured\n }\n return detectSystemLanguage()\n}\n\nexport function translate(\n language: SupportedLanguage,\n key: MessageKey,\n values: Record<string, string | number> = {},\n): string {\n const template = messages[language][key] ?? messages.en[key]\n return template.replace(/\\{(\\w+)\\}/gu, (_, name: string) => String(values[name] ?? ''))\n}\n\nexport function localizeErrorMessage(error: unknown, language: SupportedLanguage): string {\n const message = error instanceof Error ? error.message : String(error)\n if (language === 'en') {\n return message\n }\n const mapped = translateKnownError(message, language)\n return mapped ?? message\n}\n\nfunction translateKnownError(message: string, language: SupportedLanguage): string | null {\n if (message === 'Relay bootstrap token does not match public key') {\n return translate(language, 'error.relayPublicKeyMismatch')\n }\n if (message === 'Relay did not return a valid install challenge') {\n return translate(language, 'error.relayChallengeInvalid')\n }\n if (message === 'Relay did not return a valid link_id') {\n return translate(language, 'error.relayLinkInvalid')\n }\n if (message === 'Relay returned an empty response') {\n return translate(language, 'error.relayEmpty')\n }\n const portInUse = /^listen EADDRINUSE: address already in use .*:(?<port>\\d+)$/u.exec(message)\n if (portInUse?.groups?.port) {\n return translate(language, 'error.portInUse', { port: portInUse.groups.port })\n }\n const serverHttp = /^HermesPilot Server request failed with HTTP (?<status>\\d+)$/u.exec(message)\n if (serverHttp?.groups?.status) {\n return translate(language, 'error.serverHttp', { status: serverHttp.groups.status })\n }\n if (message.includes('Pairing requires HermesPilot Server and Relay')) {\n return [translate(language, 'error.pairingRequires'), translate(language, 'error.pairingRequires.detail')].join('\\n')\n }\n return null\n}\n\nfunction parseLanguage(value: string | null | undefined): SupportedLanguage | null {\n const normalized = value?.trim().replace('_', '-').toLowerCase()\n if (!normalized || normalized === 'auto' || normalized === 'c' || normalized === 'posix') {\n return null\n }\n if (normalized.startsWith('zh')) {\n return 'zh-CN'\n }\n if (normalized.startsWith('en')) {\n return 'en'\n }\n return null\n}\n"],"mappings":"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","names":["mkdir","readFile","rm","writeFile","path","mkdir","rm","mkdir","rm","mkdir","path","writeFile","rm","readFile","autostart"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/autostart/autostart.ts","../../src/daemon/process.ts","../../src/daemon/service.ts","../../src/relay/control-client.ts","../../src/i18n.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander'\nimport qrcode from 'qrcode-terminal'\nimport { LINK_COMMAND, LINK_VERSION } from '../constants.js'\nimport { enableAutostart, disableAutostart, getAutostartStatus } from '../autostart/autostart.js'\nimport { loadConfig } from '../config/config.js'\nimport {\n getDaemonStatus,\n startDaemonProcess,\n stopDaemonProcess,\n daemonLogFile,\n probeLocalLinkService,\n} from '../daemon/process.js'\nimport { startLinkService, type LinkService } from '../daemon/service.js'\nimport { ensureHermesApiServerKey } from '../hermes/config.js'\nimport { ensureIdentity, getIdentityStatus, loadIdentity } from '../identity/identity.js'\nimport { detectSystemLanguage, localizeErrorMessage, resolveLanguage, translate } from '../i18n.js'\nimport { clearPairingClaim, preparePairing, readPairingClaim } from '../pairing/pairing.js'\nimport { getLinkLogFile } from '../runtime/logger.js'\nimport { resolveRuntimePaths, type RuntimePaths } from '../runtime/paths.js'\n\nconst program = new Command()\nconst helpLanguage = detectSystemLanguage()\nconst helpText = translate.bind(null, helpLanguage)\n\nprogram\n .name(LINK_COMMAND)\n .description(helpText('program.description'))\n .version(LINK_VERSION, '-v, --version', helpText('program.version'))\n\nprogram\n .command('status')\n .option('--json', helpText('status.json'))\n .description(helpText('status.description'))\n .action(async (options: { json?: boolean }) => {\n const paths = resolveRuntimePaths()\n const [identity, config] = await Promise.all([loadIdentity(paths), loadConfig(paths)])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const payload = {\n version: LINK_VERSION,\n runtimeHome: paths.homeDir,\n paired: Boolean(identity?.link_id),\n mode: identity?.link_id ? 'paired' : 'local-only',\n port: config.port,\n identity: identity ? getIdentityStatus(identity) : null,\n relay: {\n configured: Boolean(config.relayBaseUrl),\n connected: false,\n },\n }\n if (options.json) {\n console.log(JSON.stringify(payload, null, 2))\n return\n }\n console.log(`Hermes Link ${payload.version}`)\n console.log(t('status.runtime', { value: payload.runtimeHome }))\n console.log(t('status.mode', { value: payload.mode }))\n console.log(t('status.port', { value: payload.port }))\n console.log(t('status.linkId', { value: payload.identity?.linkId ?? t('status.notPaired') }))\n })\n\nprogram\n .command('start')\n .description(helpText('start.description'))\n .action(async () => {\n const [config, status] = await Promise.all([loadConfig(), getDaemonStatus()])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n if (status.running && status.pid) {\n console.log(t('start.alreadyRunning', { pid: status.pid }))\n return\n }\n const nextStatus = await startDaemonProcess()\n console.log(t('start.backgroundStarted', { pid: nextStatus.pid ?? 'unknown' }))\n })\n\nprogram\n .command('stop')\n .description(helpText('stop.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const before = await getDaemonStatus()\n if (!before.running) {\n console.log(t('stop.notRunning'))\n return\n }\n await stopDaemonProcess()\n console.log(t('stop.stopped'))\n })\n\nprogram\n .command('restart')\n .description(helpText('restart.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n await stopDaemonProcess()\n const status = await startDaemonProcess()\n console.log(t('start.backgroundStarted', { pid: status.pid ?? 'unknown' }))\n })\n\nprogram\n .command('daemon')\n .option('--foreground', 'run in foreground')\n .description(helpText('daemon.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const service = await startLinkService({ writePidFile: true })\n console.log(t('daemon.foreground'))\n await waitForShutdown(async () => {\n await service.close()\n })\n })\n\nprogram\n .command('pair')\n .description(helpText('pair.description'))\n .action(async () => {\n const paths = resolveRuntimePaths()\n const config = await loadConfig(paths)\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n console.log(t('pair.preparing'))\n console.log(t('pair.server', { url: config.serverBaseUrl }))\n console.log(t('pair.relay', { url: config.relayBaseUrl }))\n await ensureIdentity(paths)\n const probeBeforePair = await probeLocalLinkService({ port: config.port })\n const prepared = await preparePairing(paths)\n await clearPairingClaim(prepared.sessionId, paths)\n const probe = await probeLocalLinkService({ port: config.port, linkId: prepared.linkId })\n if (probe.reachable && !probe.reusable) {\n throw new Error(\n `Local Hermes Link on port ${config.port} belongs to ${probe.linkId ?? 'an unassigned Link'}, not ${prepared.linkId}.`,\n )\n }\n const reusedRunningService = probe.reusable\n const restartReusedServiceAfterClaim = reusedRunningService && !probeBeforePair.linkId\n const service: LinkService | null = reusedRunningService ? null : await startLinkService({ paths })\n const qrValue = JSON.stringify(prepared.qrPayload)\n console.log(t('pair.linkId', { value: prepared.linkId }))\n console.log(t('pair.code', { value: prepared.code }))\n console.log(t('pair.localApi', { port: config.port }))\n console.log(t('pair.scan'))\n qrcode.generate(qrValue, { small: true })\n console.log(t('pair.expires'))\n const result = await waitForPairingOrShutdown(prepared.sessionId, paths)\n if (service) {\n await service.close()\n }\n if (result === 'claimed') {\n await clearPairingClaim(prepared.sessionId, paths)\n console.log(t(reusedRunningService ? 'pair.claimedRunning' : 'pair.claimed'))\n try {\n const autostart = await enableAutostart()\n if (autostart.supported && autostart.enabled) {\n console.log(t('autostart.enabled', { method: autostart.method, path: autostart.filePath ?? '' }))\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.log(t('pair.autostartFailed', { message }))\n }\n if (restartReusedServiceAfterClaim) {\n await stopDaemonProcess(paths)\n const status = await startDaemonProcess(paths)\n console.log(t('start.backgroundStarted', { pid: status.pid ?? 'unknown' }))\n } else if (!reusedRunningService) {\n const status = await startDaemonProcess(paths)\n console.log(t('start.backgroundStarted', { pid: status.pid ?? 'unknown' }))\n }\n }\n })\n\nconst autostart = program.command('autostart').description(helpText('autostart.description'))\n\nautostart\n .command('on')\n .description(helpText('autostart.on.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const status = await enableAutostart()\n if (!status.supported) {\n console.log(t('autostart.unsupported'))\n return\n }\n console.log(t('autostart.enabled', { method: status.method, path: status.filePath ?? '' }))\n })\n\nautostart\n .command('off')\n .description(helpText('autostart.off.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n await disableAutostart()\n console.log(t('autostart.disabled'))\n })\n\nautostart\n .command('status')\n .description(helpText('autostart.status.description'))\n .action(async () => {\n const config = await loadConfig()\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const status = await getAutostartStatus()\n if (!status.supported) {\n console.log(t('autostart.unsupported'))\n return\n }\n console.log(\n t(status.enabled ? 'autostart.status.enabled' : 'autostart.status.disabled', {\n method: status.method,\n path: status.filePath ?? '',\n }),\n )\n })\n\nprogram\n .command('logs')\n .description(helpText('logs.description'))\n .action(async () => {\n const paths = resolveRuntimePaths()\n const config = await loadConfig(paths)\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n console.log(t('logs.servicePath', { path: getLinkLogFile(paths) }))\n console.log(t('logs.daemonPath', { path: daemonLogFile(paths) }))\n })\n\nprogram\n .command('doctor')\n .description(helpText('doctor.description'))\n .action(async () => {\n const [identity, config] = await Promise.all([ensureIdentity(), loadConfig()])\n const language = resolveLanguage(config.language)\n const t = translate.bind(null, language)\n const hermesConfig = await ensureHermesApiServerKey()\n console.log(t('doctor.identityOk'))\n console.log(t('doctor.installId', { value: identity.install_id }))\n console.log(t('doctor.linkId', { value: identity.link_id ?? t('doctor.notAssigned') }))\n if (hermesConfig.notice) {\n console.log(hermesConfig.notice)\n if (hermesConfig.backupPath) {\n console.log(`Hermes config backup: ${hermesConfig.backupPath}`)\n }\n }\n })\n\nprogram.parseAsync(process.argv).catch(async (error) => {\n const language = await loadCliLanguage().catch(() => detectSystemLanguage())\n console.error(localizeErrorMessage(error, language))\n process.exitCode = 1\n})\n\nasync function loadCliLanguage() {\n const config = await loadConfig()\n return resolveLanguage(config.language)\n}\n\nasync function waitForShutdown(cleanup: () => Promise<void>): Promise<void> {\n await new Promise<void>((resolve) => {\n const stop = () => resolve()\n process.once('SIGINT', stop)\n process.once('SIGTERM', stop)\n })\n await cleanup()\n}\n\nasync function waitForPairingOrShutdown(\n sessionId: string,\n paths: RuntimePaths,\n): Promise<'claimed' | 'shutdown'> {\n let shutdownRequested = false\n const stop = () => {\n shutdownRequested = true\n }\n process.once('SIGINT', stop)\n process.once('SIGTERM', stop)\n try {\n while (!shutdownRequested) {\n const record = await readPairingClaim(sessionId, paths)\n if (record) {\n return 'claimed'\n }\n await sleep(250)\n }\n return 'shutdown'\n } finally {\n process.off('SIGINT', stop)\n process.off('SIGTERM', stop)\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","import { execFile } from 'node:child_process'\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { promisify } from 'node:util'\nimport { currentCliScriptPath } from '../daemon/process.js'\n\nconst execFileAsync = promisify(execFile)\nconst MACOS_LABEL = 'com.hermespilot.link'\n\nexport interface AutostartStatus {\n supported: boolean\n enabled: boolean\n method: 'launchd' | 'systemd-user' | 'xdg-autostart' | 'windows-startup' | 'unsupported'\n filePath: string | null\n}\n\nexport async function enableAutostart(): Promise<AutostartStatus> {\n const definition = await resolveAutostartDefinition()\n if (!definition) {\n return unsupportedStatus()\n }\n await mkdir(path.dirname(definition.filePath), { recursive: true, mode: 0o700 })\n await writeFile(definition.filePath, definition.content, { mode: 0o600 })\n if (definition.method === 'systemd-user') {\n await execFileAsync('systemctl', ['--user', 'enable', path.basename(definition.filePath)]).catch(async () => {\n await rm(definition.filePath, { force: true }).catch(() => undefined)\n const fallback = xdgAutostartDefinition()\n await mkdir(path.dirname(fallback.filePath), { recursive: true, mode: 0o700 })\n await writeFile(fallback.filePath, fallback.content, { mode: 0o600 })\n })\n }\n return await getAutostartStatus()\n}\n\nexport async function disableAutostart(): Promise<AutostartStatus> {\n const definitions = await allAutostartDefinitions()\n for (const definition of definitions) {\n if (definition.method === 'systemd-user') {\n await execFileAsync('systemctl', ['--user', 'disable', path.basename(definition.filePath)]).catch(() => undefined)\n }\n await rm(definition.filePath, { force: true }).catch(() => undefined)\n }\n return await getAutostartStatus()\n}\n\nexport async function getAutostartStatus(): Promise<AutostartStatus> {\n const definitions = await allAutostartDefinitions()\n if (definitions.length === 0) {\n return unsupportedStatus()\n }\n for (const definition of definitions) {\n const content = await readFile(definition.filePath, 'utf8').catch(() => null)\n if (content !== null) {\n return {\n supported: true,\n enabled: true,\n method: definition.method,\n filePath: definition.filePath,\n }\n }\n }\n const primary = definitions[0]\n return {\n supported: true,\n enabled: false,\n method: primary.method,\n filePath: primary.filePath,\n }\n}\n\nasync function resolveAutostartDefinition(): Promise<AutostartDefinition | null> {\n if (process.platform === 'darwin') {\n return launchdDefinition()\n }\n if (process.platform === 'win32') {\n return windowsStartupDefinition()\n }\n if (process.platform === 'linux') {\n return await hasSystemctlUser() ? systemdUserDefinition() : xdgAutostartDefinition()\n }\n return null\n}\n\nasync function allAutostartDefinitions(): Promise<AutostartDefinition[]> {\n if (process.platform === 'darwin') {\n return [launchdDefinition()]\n }\n if (process.platform === 'win32') {\n return [windowsStartupDefinition()]\n }\n if (process.platform === 'linux') {\n return [systemdUserDefinition(), xdgAutostartDefinition()]\n }\n return []\n}\n\nasync function hasSystemctlUser(): Promise<boolean> {\n try {\n await execFileAsync('systemctl', ['--user', 'show-environment'])\n return true\n } catch {\n return false\n }\n}\n\nfunction launchdDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${MACOS_LABEL}.plist`)\n return {\n method: 'launchd',\n filePath,\n content: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${MACOS_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${xmlEscape(process.execPath)}</string>\n <string>${xmlEscape(currentCliScriptPath())}</string>\n <string>daemon</string>\n <string>--foreground</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <false/>\n <key>StandardOutPath</key>\n <string>${xmlEscape(path.join(os.homedir(), '.hermeslink', 'logs', 'daemon.log'))}</string>\n <key>StandardErrorPath</key>\n <string>${xmlEscape(path.join(os.homedir(), '.hermeslink', 'logs', 'daemon.log'))}</string>\n</dict>\n</plist>\n`,\n }\n}\n\nfunction systemdUserDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), '.config', 'systemd', 'user', 'hermeslink.service')\n return {\n method: 'systemd-user',\n filePath,\n content: `[Unit]\nDescription=Hermes Link\nAfter=network-online.target\n\n[Service]\nType=simple\nExecStart=${systemdQuote(process.execPath)} ${systemdQuote(currentCliScriptPath())} daemon --foreground\nRestart=no\n\n[Install]\nWantedBy=default.target\n`,\n }\n}\n\nfunction xdgAutostartDefinition(): AutostartDefinition {\n const filePath = path.join(os.homedir(), '.config', 'autostart', 'hermeslink.desktop')\n return {\n method: 'xdg-autostart',\n filePath,\n content: `[Desktop Entry]\nType=Application\nName=Hermes Link\nExec=${desktopQuote(process.execPath)} ${desktopQuote(currentCliScriptPath())} daemon --foreground\nTerminal=false\nX-GNOME-Autostart-enabled=true\n`,\n }\n}\n\nfunction windowsStartupDefinition(): AutostartDefinition {\n const appData = process.env.APPDATA ?? path.join(os.homedir(), 'AppData', 'Roaming')\n const filePath = path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'HermesLink.cmd')\n return {\n method: 'windows-startup',\n filePath,\n content: `@echo off\\r\\nstart \"\" /min \"${process.execPath}\" \"${currentCliScriptPath()}\" daemon --foreground\\r\\n`,\n }\n}\n\nfunction unsupportedStatus(): AutostartStatus {\n return {\n supported: false,\n enabled: false,\n method: 'unsupported',\n filePath: null,\n }\n}\n\nfunction xmlEscape(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;')\n}\n\nfunction systemdQuote(value: string): string {\n return `\"${value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`\n}\n\nfunction desktopQuote(value: string): string {\n return `\"${value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')}\"`\n}\n\ninterface AutostartDefinition {\n method: AutostartStatus['method']\n filePath: string\n content: string\n}\n","import { spawn } from 'node:child_process'\nimport { mkdir, open, readFile, rm } from 'node:fs/promises'\nimport path from 'node:path'\nimport { pidFilePath } from './service.js'\nimport { resolveRuntimePaths, type RuntimePaths } from '../runtime/paths.js'\n\nexport interface DaemonStatus {\n running: boolean\n pid: number | null\n pidFile: string\n logFile: string\n}\n\nexport interface LocalLinkServiceProbe {\n reachable: boolean\n reusable: boolean\n linkId: string | null\n version: string | null\n}\n\nexport async function startDaemonProcess(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const status = await getDaemonStatus(paths)\n if (status.running) {\n return status\n }\n await mkdir(paths.logsDir, { recursive: true, mode: 0o700 })\n await mkdir(paths.runDir, { recursive: true, mode: 0o700 })\n const log = await open(daemonLogFile(paths), 'a', 0o600)\n const scriptPath = currentCliScriptPath()\n const child = spawn(process.execPath, [scriptPath, 'daemon', '--foreground'], {\n detached: true,\n stdio: ['ignore', log.fd, log.fd],\n env: process.env,\n })\n child.unref()\n await log.close()\n for (let index = 0; index < 12; index += 1) {\n await wait(250)\n const next = await getDaemonStatus(paths)\n if (next.running) {\n return next\n }\n }\n return await getDaemonStatus(paths)\n}\n\nexport async function probeLocalLinkService(options: {\n port: number\n linkId?: string\n timeoutMs?: number\n}): Promise<LocalLinkServiceProbe> {\n const unreachable: LocalLinkServiceProbe = {\n reachable: false,\n reusable: false,\n linkId: null,\n version: null,\n }\n let response: Response\n try {\n response = await fetch(`http://127.0.0.1:${options.port}/api/v1/bootstrap`, {\n headers: { accept: 'application/json' },\n signal: AbortSignal.timeout(options.timeoutMs ?? 1_000),\n })\n } catch {\n return unreachable\n }\n if (!response.ok) {\n return unreachable\n }\n const payload = (await response.json().catch(() => null)) as Record<string, unknown> | null\n if (!payload || payload.api_version !== 1) {\n return unreachable\n }\n const linkId = typeof payload.link_id === 'string' ? payload.link_id : null\n return {\n reachable: true,\n reusable: options.linkId ? linkId === options.linkId : true,\n linkId,\n version: typeof payload.version === 'string' ? payload.version : null,\n }\n}\n\nexport async function stopDaemonProcess(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const status = await getDaemonStatus(paths)\n if (!status.running || !status.pid) {\n return status\n }\n try {\n process.kill(status.pid, 'SIGTERM')\n } catch {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n return await getDaemonStatus(paths)\n }\n for (let index = 0; index < 20; index += 1) {\n await wait(250)\n if (!isProcessAlive(status.pid)) {\n break\n }\n }\n if (!isProcessAlive(status.pid)) {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n }\n return await getDaemonStatus(paths)\n}\n\nexport async function getDaemonStatus(paths: RuntimePaths = resolveRuntimePaths()): Promise<DaemonStatus> {\n const pidFile = pidFilePath(paths)\n const pid = await readPid(pidFile)\n if (pid && !isProcessAlive(pid)) {\n await rm(pidFile, { force: true }).catch(() => undefined)\n return {\n running: false,\n pid: null,\n pidFile,\n logFile: daemonLogFile(paths),\n }\n }\n return {\n running: Boolean(pid),\n pid,\n pidFile,\n logFile: daemonLogFile(paths),\n }\n}\n\nexport function daemonLogFile(paths: RuntimePaths = resolveRuntimePaths()): string {\n return path.join(paths.logsDir, 'daemon.log')\n}\n\nexport function currentCliScriptPath(): string {\n return process.argv[1]\n}\n\nasync function readPid(filePath: string): Promise<number | null> {\n const raw = await readFile(filePath, 'utf8').catch(() => null)\n if (!raw) {\n return null\n }\n const pid = Number.parseInt(raw.trim(), 10)\n return Number.isInteger(pid) && pid > 0 ? pid : null\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","import { createServer, type Server } from 'node:http'\nimport { mkdir, rm, writeFile } from 'node:fs/promises'\nimport { createApp, type CreateAppOptions } from '../http/app.js'\nimport { loadConfig } from '../config/config.js'\nimport { loadIdentity } from '../identity/identity.js'\nimport { connectRelayControl, type RelayControlClient } from '../relay/control-client.js'\nimport { createFileLogger } from '../runtime/logger.js'\nimport { resolveRuntimePaths, type RuntimePaths } from '../runtime/paths.js'\n\nexport interface LinkService {\n close(): Promise<void>\n}\n\nexport interface StartLinkServiceOptions extends CreateAppOptions {\n paths?: RuntimePaths\n writePidFile?: boolean\n relayMaxReconnectAttempts?: number\n}\n\nexport async function startLinkService(options: StartLinkServiceOptions = {}): Promise<LinkService> {\n const paths = options.paths ?? resolveRuntimePaths()\n const logger = createFileLogger({ paths })\n const [identity, config] = await Promise.all([loadIdentity(paths), loadConfig(paths)])\n await logger.info('service_starting', {\n port: config.port,\n mode: identity?.link_id ? 'paired' : 'local-only',\n })\n const app = await createApp({ paths, logger, onPairingClaimed: options.onPairingClaimed })\n const server = createServer(app.callback())\n try {\n await listenServer(server, config.port)\n } catch (error) {\n await logger.error('service_start_failed', {\n port: config.port,\n error: error instanceof Error ? error.message : String(error),\n })\n await logger.flush()\n throw error\n }\n server.on('error', (error) => {\n void logger.error('service_error', { error: error.message })\n })\n void logger.info('service_started', {\n port: config.port,\n link_id: identity?.link_id ?? null,\n })\n let relay: RelayControlClient | null = null\n if (identity?.link_id) {\n relay = connectRelayControl({\n relayBaseUrl: config.relayBaseUrl,\n linkId: identity.link_id,\n localPort: config.port,\n maxReconnectAttempts: options.relayMaxReconnectAttempts ?? 5,\n backoffBaseMs: 1_000,\n backoffMaxMs: 30_000,\n onStatus: (status) => {\n void logger.info('relay_status', status)\n },\n })\n } else {\n void logger.info('relay_skipped', { reason: 'link_not_paired' })\n }\n if (options.writePidFile) {\n await writePidFile(paths)\n }\n return {\n async close() {\n relay?.close()\n await closeServer(server)\n await logger.info('service_stopped')\n await logger.flush()\n if (options.writePidFile) {\n await rm(pidFilePath(paths), { force: true }).catch(() => undefined)\n }\n },\n }\n}\n\nexport function pidFilePath(paths: RuntimePaths = resolveRuntimePaths()): string {\n return `${paths.runDir}/hermeslink.pid`\n}\n\nasync function writePidFile(paths: RuntimePaths): Promise<void> {\n await mkdir(paths.runDir, { recursive: true, mode: 0o700 })\n await writeFile(pidFilePath(paths), `${process.pid}\\n`, { mode: 0o600 })\n}\n\nasync function closeServer(server: Server): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error)\n return\n }\n resolve()\n })\n })\n}\n\nasync function listenServer(server: Server, port: number): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n server.off('error', onError)\n server.off('listening', onListening)\n }\n const onError = (error: Error) => {\n cleanup()\n reject(error)\n }\n const onListening = () => {\n cleanup()\n resolve()\n }\n\n server.once('error', onError)\n server.once('listening', onListening)\n server.listen(port)\n })\n}\n","import WebSocket from 'ws'\nimport { LINK_VERSION } from '../constants.js'\n\ninterface RelayRequestFrame {\n type: 'http.request'\n id: string\n method: string\n path: string\n headers?: Record<string, string>\n bodyBase64?: string | null\n}\n\ninterface RelayCancelFrame {\n type: 'http.cancel'\n id: string\n}\n\ntype RelayFrame = RelayRequestFrame | RelayCancelFrame\n\nexport interface RelayControlClient {\n close(): void\n}\n\nexport function connectRelayControl(options: {\n relayBaseUrl: string\n linkId: string\n localPort: number\n maxReconnectAttempts?: number\n backoffBaseMs?: number\n backoffMaxMs?: number\n onStatus?: (status: { state: 'connecting' | 'connected' | 'disconnected' | 'retrying' | 'failed'; attempt: number; message?: string }) => void\n}): RelayControlClient {\n const wsUrl = new URL(`${options.relayBaseUrl.replace(/\\/+$/u, '')}/api/v1/relay/link/connect`)\n wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:'\n wsUrl.searchParams.set('link_id', options.linkId)\n\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 5\n const backoffBaseMs = options.backoffBaseMs ?? 1_000\n const backoffMaxMs = options.backoffMaxMs ?? 30_000\n let reconnectAttempts = 0\n let closedByUser = false\n let socket: WebSocket | null = null\n let retryTimer: ReturnType<typeof setTimeout> | null = null\n let abortControllers = new Map<string, AbortController>()\n\n const connect = () => {\n options.onStatus?.({ state: 'connecting', attempt: reconnectAttempts })\n socket = new WebSocket(wsUrl, {\n headers: {\n 'x-hermes-link-version': LINK_VERSION,\n },\n })\n socket.on('open', () => {\n reconnectAttempts = 0\n options.onStatus?.({ state: 'connected', attempt: reconnectAttempts })\n })\n socket.on('message', (raw) => {\n if (!socket || (typeof raw !== 'string' && !Buffer.isBuffer(raw))) {\n return\n }\n void handleFrame(socket, String(raw), options.localPort, abortControllers).catch((error) => {\n const message = error instanceof Error ? error.message : 'Relay request failed'\n socket?.send(JSON.stringify({ type: 'http.error', id: 'unknown', status: 502, message }))\n })\n })\n socket.on('error', (error) => {\n const message = error instanceof Error ? error.message : 'Relay websocket error'\n options.onStatus?.({ state: 'disconnected', attempt: reconnectAttempts, message })\n })\n socket.on('close', () => {\n abortAll(abortControllers)\n abortControllers = new Map<string, AbortController>()\n if (closedByUser) {\n options.onStatus?.({ state: 'disconnected', attempt: reconnectAttempts })\n return\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n options.onStatus?.({ state: 'failed', attempt: reconnectAttempts, message: 'Relay reconnect attempts exhausted' })\n return\n }\n reconnectAttempts += 1\n const delay = computeBackoffMs(reconnectAttempts, backoffBaseMs, backoffMaxMs)\n options.onStatus?.({ state: 'retrying', attempt: reconnectAttempts, message: `Retrying in ${delay}ms` })\n retryTimer = setTimeout(connect, delay)\n retryTimer.unref?.()\n })\n }\n\n connect()\n\n return {\n close() {\n closedByUser = true\n if (retryTimer) {\n clearTimeout(retryTimer)\n }\n abortAll(abortControllers)\n socket?.close()\n },\n }\n}\n\nfunction abortAll(abortControllers: Map<string, AbortController>): void {\n for (const controller of abortControllers.values()) {\n controller.abort()\n }\n abortControllers.clear()\n}\n\nfunction computeBackoffMs(attempt: number, baseMs: number, maxMs: number): number {\n const exponential = Math.min(maxMs, baseMs * 2 ** Math.max(0, attempt - 1))\n const jitter = Math.floor(Math.random() * Math.min(1_000, exponential * 0.2))\n return exponential + jitter\n}\n\nasync function handleFrame(\n socket: WebSocket,\n raw: string,\n localPort: number,\n abortControllers: Map<string, AbortController>,\n): Promise<void> {\n const frame = JSON.parse(raw) as RelayFrame\n if (frame.type === 'http.cancel') {\n abortControllers.get(frame.id)?.abort()\n abortControllers.delete(frame.id)\n return\n }\n if (frame.type !== 'http.request') {\n return\n }\n const abortController = new AbortController()\n abortControllers.set(frame.id, abortController)\n try {\n const response = await fetch(`http://127.0.0.1:${localPort}${frame.path}`, {\n method: frame.method,\n headers: frame.headers ?? {},\n body: frame.bodyBase64 ? Buffer.from(frame.bodyBase64, 'base64') : undefined,\n signal: abortController.signal,\n })\n const headers = Object.fromEntries(response.headers.entries())\n const contentType = response.headers.get('content-type') ?? ''\n if (response.body && contentType.includes('text/event-stream')) {\n socket.send(JSON.stringify({ type: 'http.stream.start', id: frame.id, status: response.status, headers }))\n const reader = response.body.getReader()\n while (true) {\n const next = await reader.read()\n if (next.done) {\n break\n }\n socket.send(JSON.stringify({ type: 'http.stream.chunk', id: frame.id, bodyBase64: Buffer.from(next.value).toString('base64') }))\n }\n socket.send(JSON.stringify({ type: 'http.stream.end', id: frame.id }))\n return\n }\n const body = Buffer.from(await response.arrayBuffer()).toString('base64')\n socket.send(JSON.stringify({ type: 'http.response', id: frame.id, status: response.status, headers, bodyBase64: body }))\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Relay request failed'\n socket.send(JSON.stringify({ type: 'http.error', id: frame.id, status: 502, message }))\n } finally {\n abortControllers.delete(frame.id)\n }\n}\n","export type SupportedLanguage = 'zh-CN' | 'en'\nexport type ConfiguredLanguage = SupportedLanguage | 'auto'\n\nconst messages = {\n en: {\n 'program.description': 'Hermes Link companion service',\n 'program.version': 'print Hermes Link version',\n 'status.description': 'Show local Hermes Link status',\n 'status.json': 'print machine-readable status',\n 'status.runtime': 'Runtime: {value}',\n 'status.mode': 'Mode: {value}',\n 'status.port': 'Local port: {value}',\n 'status.linkId': 'Link ID: {value}',\n 'status.notPaired': 'not paired',\n 'start.description': 'Start Hermes Link daemon',\n 'start.backgroundStarted': 'Hermes Link is running in the background. PID: {pid}',\n 'start.alreadyRunning': 'Hermes Link is already running. PID: {pid}',\n 'start.notPaired': 'Hermes Link is not paired yet. Starting in local-only maintenance mode.',\n 'start.notPaired.detail':\n 'Relay, Server polling, and LAN entrypoints stay disabled until you run `hermeslink pair`.',\n 'start.listening': 'Hermes Link API listening on http://127.0.0.1:{port}',\n 'start.relayConnecting': 'Relay control connecting for {linkId}',\n 'stop.description': 'Stop the background Hermes Link daemon',\n 'stop.stopped': 'Hermes Link stopped.',\n 'stop.notRunning': 'Hermes Link is not running.',\n 'restart.description': 'Restart the background Hermes Link daemon',\n 'daemon.description': 'Run Hermes Link in the foreground',\n 'daemon.foreground': 'Hermes Link foreground daemon is running. Press Ctrl+C to stop.',\n 'logs.description': 'Show Hermes Link log paths',\n 'logs.servicePath': 'Service log: {path}',\n 'logs.daemonPath': 'Daemon stdout/stderr log: {path}',\n 'autostart.description': 'Manage boot autostart',\n 'autostart.on.description': 'Enable boot autostart',\n 'autostart.off.description': 'Disable boot autostart',\n 'autostart.status.description': 'Show boot autostart status',\n 'autostart.enabled': 'Boot autostart enabled via {method}: {path}',\n 'autostart.disabled': 'Boot autostart disabled.',\n 'autostart.status.enabled': 'Boot autostart: enabled via {method}: {path}',\n 'autostart.status.disabled': 'Boot autostart: disabled. Method: {method}. File: {path}',\n 'autostart.unsupported': 'Boot autostart is not supported on this platform yet.',\n 'pair.description': 'Create a Hermes Link pairing session',\n 'pair.preparing': 'Preparing pairing session through HermesPilot Server and Relay...',\n 'pair.server': 'Server: {url}',\n 'pair.relay': 'Relay: {url}',\n 'pair.linkId': 'Hermes Link ID: {value}',\n 'pair.code': 'Pairing code: {value}',\n 'pair.localApi': 'Local API: http://127.0.0.1:{port}',\n 'pair.scan': 'Scan this QR code with the HermesPilot App:',\n 'pair.expires': 'Pairing expires in 10 minutes. Press Ctrl+C to cancel waiting.',\n 'pair.claimed': 'Pairing succeeded. Starting Hermes Link in the background...',\n 'pair.claimedRunning': 'Pairing succeeded. Hermes Link is already running in the background.',\n 'pair.autostartFailed': 'Pairing succeeded, but boot autostart could not be enabled: {message}',\n 'doctor.description': 'Run local diagnostics',\n 'doctor.identityOk': 'Runtime identity: OK',\n 'doctor.installId': 'Install ID: {value}',\n 'doctor.linkId': 'Link ID: {value}',\n 'doctor.notAssigned': 'not assigned',\n 'error.relayPublicKeyMismatch':\n 'Relay rejected the pairing request because the Server-issued bootstrap token does not match this Link public key. Make sure Server and Relay are deployed with the same bootstrap key configuration, then run `hermeslink pair` again.',\n 'error.relayChallengeInvalid': 'Relay did not return a valid install challenge.',\n 'error.relayLinkInvalid': 'Relay did not return a valid link_id.',\n 'error.relayEmpty': 'Relay returned an empty response.',\n 'error.serverHttp': 'HermesPilot Server request failed with HTTP {status}.',\n 'error.portInUse':\n 'Local port {port} is already in use by another process. Stop that process or change the Hermes Link port, then run `hermeslink pair` again.',\n 'error.pairingRequires':\n 'Pairing needs HermesPilot Server and Relay, but this command could not start a complete pairing session.',\n 'error.pairingRequires.detail':\n '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.',\n },\n 'zh-CN': {\n 'program.description': 'Hermes Link 本地伴随服务',\n 'program.version': '输出 Hermes Link 版本号',\n 'status.description': '查看本机 Hermes Link 状态',\n 'status.json': '输出机器可读的状态 JSON',\n 'status.runtime': '运行目录:{value}',\n 'status.mode': '模式:{value}',\n 'status.port': '本地端口:{value}',\n 'status.linkId': 'Link ID:{value}',\n 'status.notPaired': '尚未配对',\n 'start.description': '启动 Hermes Link 服务',\n 'start.backgroundStarted': 'Hermes Link 已在后台运行。PID:{pid}',\n 'start.alreadyRunning': 'Hermes Link 已经在运行。PID:{pid}',\n 'start.notPaired': 'Hermes Link 还没有配对,将以本地维护模式启动。',\n 'start.notPaired.detail': '在你运行 `hermeslink pair` 前,Relay、Server 轮询和局域网入口都会保持关闭。',\n 'start.listening': 'Hermes Link API 正在监听 http://127.0.0.1:{port}',\n 'start.relayConnecting': '正在为 {linkId} 连接 Relay 控制通道',\n 'stop.description': '停止后台 Hermes Link 服务',\n 'stop.stopped': 'Hermes Link 已停止。',\n 'stop.notRunning': 'Hermes Link 没有在运行。',\n 'restart.description': '重启后台 Hermes Link 服务',\n 'daemon.description': '以前台方式运行 Hermes Link',\n 'daemon.foreground': 'Hermes Link 前台服务正在运行。按 Ctrl+C 停止。',\n 'logs.description': '显示 Hermes Link 日志路径',\n 'logs.servicePath': '服务日志:{path}',\n 'logs.daemonPath': 'Daemon 标准输出/错误日志:{path}',\n 'autostart.description': '管理开机自启',\n 'autostart.on.description': '启用开机自启',\n 'autostart.off.description': '关闭开机自启',\n 'autostart.status.description': '查看开机自启状态',\n 'autostart.enabled': '已启用开机自启,方式:{method},文件:{path}',\n 'autostart.disabled': '已关闭开机自启。',\n 'autostart.status.enabled': '开机自启:已启用,方式:{method},文件:{path}',\n 'autostart.status.disabled': '开机自启:未启用。方式:{method},文件:{path}',\n 'autostart.unsupported': '当前平台暂不支持开机自启。',\n 'pair.description': '创建 Hermes Link 配对会话',\n 'pair.preparing': '正在通过 HermesPilot Server 和 Relay 创建配对会话...',\n 'pair.server': 'Server:{url}',\n 'pair.relay': 'Relay:{url}',\n 'pair.linkId': 'Hermes Link ID:{value}',\n 'pair.code': '配对码:{value}',\n 'pair.localApi': '本地 API:http://127.0.0.1:{port}',\n 'pair.scan': '请使用 HermesPilot App 扫描这个二维码:',\n 'pair.expires': '配对会话 10 分钟后过期。按 Ctrl+C 退出等待。',\n 'pair.claimed': '配对已成功。正在把 Hermes Link 切换到后台运行...',\n 'pair.claimedRunning': '配对已成功。Hermes Link 已在后台持续运行。',\n 'pair.autostartFailed': '配对已成功,但启用开机自启失败:{message}',\n 'doctor.description': '运行本机诊断',\n 'doctor.identityOk': '运行身份:正常',\n 'doctor.installId': 'Install ID:{value}',\n 'doctor.linkId': 'Link ID:{value}',\n 'doctor.notAssigned': '尚未分配',\n 'error.relayPublicKeyMismatch':\n 'Relay 拒绝了配对请求:Server 签发的 bootstrap token 与本机 Link 公钥不匹配。请确认 Server 和 Relay 使用同一套 bootstrap key 配置,然后重新运行 `hermeslink pair`。',\n 'error.relayChallengeInvalid': 'Relay 没有返回有效的安装挑战。',\n 'error.relayLinkInvalid': 'Relay 没有返回有效的 link_id。',\n 'error.relayEmpty': 'Relay 返回了空响应。',\n 'error.serverHttp': 'HermesPilot Server 请求失败,HTTP 状态码:{status}。',\n 'error.portInUse':\n '本地端口 {port} 已被其他进程占用。请先停止占用该端口的程序,或调整 Hermes Link 端口后重新运行 `hermeslink pair`。',\n 'error.pairingRequires': '配对需要 HermesPilot Server 和 Relay,但当前命令没有能启动完整配对会话。',\n 'error.pairingRequires.detail':\n '云端服务可以是已部署且健康的;本机 Link 仍必须先向 Server 申请短期 relay bootstrap token,才能再向 Relay 申请 link_id。',\n },\n} satisfies Record<SupportedLanguage, Record<string, string>>\n\nexport type MessageKey = keyof (typeof messages)['en']\n\nexport function detectSystemLanguage(env: NodeJS.ProcessEnv = process.env): SupportedLanguage {\n const candidates = [\n env.HERMESLINK_LANG,\n env.HERMESLINK_LANGUAGE,\n env.LC_ALL,\n env.LC_MESSAGES,\n env.LANG,\n env.LANGUAGE?.split(':')[0],\n Intl.DateTimeFormat().resolvedOptions().locale,\n ]\n for (const candidate of candidates) {\n const language = parseLanguage(candidate)\n if (language) {\n return language\n }\n }\n return 'en'\n}\n\nexport function resolveLanguage(setting?: ConfiguredLanguage | string | null): SupportedLanguage {\n const configured = parseLanguage(setting)\n if (configured) {\n return configured\n }\n return detectSystemLanguage()\n}\n\nexport function translate(\n language: SupportedLanguage,\n key: MessageKey,\n values: Record<string, string | number> = {},\n): string {\n const template = messages[language][key] ?? messages.en[key]\n return template.replace(/\\{(\\w+)\\}/gu, (_, name: string) => String(values[name] ?? ''))\n}\n\nexport function localizeErrorMessage(error: unknown, language: SupportedLanguage): string {\n const message = error instanceof Error ? error.message : String(error)\n if (language === 'en') {\n return message\n }\n const mapped = translateKnownError(message, language)\n return mapped ?? message\n}\n\nfunction translateKnownError(message: string, language: SupportedLanguage): string | null {\n if (message === 'Relay bootstrap token does not match public key') {\n return translate(language, 'error.relayPublicKeyMismatch')\n }\n if (message === 'Relay did not return a valid install challenge') {\n return translate(language, 'error.relayChallengeInvalid')\n }\n if (message === 'Relay did not return a valid link_id') {\n return translate(language, 'error.relayLinkInvalid')\n }\n if (message === 'Relay returned an empty response') {\n return translate(language, 'error.relayEmpty')\n }\n const portInUse = /^listen EADDRINUSE: address already in use .*:(?<port>\\d+)$/u.exec(message)\n if (portInUse?.groups?.port) {\n return translate(language, 'error.portInUse', { port: portInUse.groups.port })\n }\n const serverHttp = /^HermesPilot Server request failed with HTTP (?<status>\\d+)$/u.exec(message)\n if (serverHttp?.groups?.status) {\n return translate(language, 'error.serverHttp', { status: serverHttp.groups.status })\n }\n if (message.includes('Pairing requires HermesPilot Server and Relay')) {\n return [translate(language, 'error.pairingRequires'), translate(language, 'error.pairingRequires.detail')].join('\\n')\n }\n return null\n}\n\nfunction parseLanguage(value: string | null | undefined): SupportedLanguage | null {\n const normalized = value?.trim().replace('_', '-').toLowerCase()\n if (!normalized || normalized === 'auto' || normalized === 'c' || normalized === 'posix') {\n return null\n }\n if (normalized.startsWith('zh')) {\n return 'zh-CN'\n }\n if (normalized.startsWith('en')) {\n return 'en'\n }\n return null\n}\n"],"mappings":"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","names":["mkdir","readFile","rm","writeFile","path","mkdir","rm","mkdir","rm","mkdir","path","writeFile","rm","readFile","autostart"]}
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "../chunk-T35GPRKF.js";
3
+ } from "../chunk-E54NSFGF.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.1.4",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "description": "Hermes Link companion service and CLI for connecting hermes-agent through HermesPilot",
6
6
  "license": "MIT",