@bulolo/hermes-link 0.3.5 → 0.3.7
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/README.md +330 -439
- package/README.zh-CN.md +733 -0
- package/dist/{chunk-EYXOHOAC.js → chunk-7IVSOP5F.js} +137 -45
- package/dist/cli/index.js +10 -5
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
- package/README.en.md +0 -630
|
@@ -1546,9 +1546,9 @@ async function discoverRouteCandidates(options) {
|
|
|
1546
1546
|
const publicIpv4s = unique(publicIps.publicIpv4s.filter(isUsablePublicIpv4)).slice(0, MAX_PUBLIC_IPV4S);
|
|
1547
1547
|
const publicIpv6s = unique(publicIps.publicIpv6s.filter(isUsablePublicIpv6)).slice(0, MAX_PUBLIC_IPV6S);
|
|
1548
1548
|
const preferredUrls = [
|
|
1549
|
+
...lanIps.map((ip) => buildDirectUrl(ip, options.port)),
|
|
1549
1550
|
...publicIpv4s.map((ip) => buildDirectUrl(ip, options.port)),
|
|
1550
|
-
...publicIpv6s.map((ip) => buildDirectUrl(ip, options.port))
|
|
1551
|
-
...lanIps.map((ip) => buildDirectUrl(ip, options.port))
|
|
1551
|
+
...publicIpv6s.map((ip) => buildDirectUrl(ip, options.port))
|
|
1552
1552
|
];
|
|
1553
1553
|
return { lanIps, publicIpv4s, publicIpv6s, preferredUrls, environment };
|
|
1554
1554
|
}
|
|
@@ -9002,10 +9002,10 @@ async function startLinkService(options) {
|
|
|
9002
9002
|
function escapeHtml(s) {
|
|
9003
9003
|
return s.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
9004
9004
|
}
|
|
9005
|
-
function formatDate(iso) {
|
|
9005
|
+
function formatDate(iso, locale = "zh-CN") {
|
|
9006
9006
|
const d = new Date(iso);
|
|
9007
9007
|
if (!Number.isFinite(d.getTime())) return iso;
|
|
9008
|
-
return d.toLocaleString(
|
|
9008
|
+
return d.toLocaleString(locale, { hour12: locale !== "zh-CN" });
|
|
9009
9009
|
}
|
|
9010
9010
|
async function renderPairingPage(options) {
|
|
9011
9011
|
const { session, claimed, version, linkId } = options;
|
|
@@ -9023,11 +9023,9 @@ async function renderPairingPage(options) {
|
|
|
9023
9023
|
const qrDataUri = `data:image/svg+xml;base64,${Buffer.from(qrSvg).toString("base64")}`;
|
|
9024
9024
|
const currentUrl = session.local_api_url.replace(/\/+$/u, "");
|
|
9025
9025
|
const expiresAtMs = Date.parse(session.expires_at);
|
|
9026
|
-
const
|
|
9027
|
-
const statusHint = claimed ? "App \u5DF2\u5B8C\u6210\u914D\u5BF9\uFF0C\u8FD9\u4E2A\u9875\u9762\u53EF\u4EE5\u5173\u95ED\u3002" : isExpired ? "\u8FD9\u6B21\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C hermeslink pair\u3002" : "\u6253\u5F00 App \u626B\u7801\uFF0C\u6216\u8005\u590D\u5236\u914D\u5BF9\u7801\u624B\u52A8\u8F93\u5165\u3002";
|
|
9028
|
-
const statusPillLabel = claimed ? "\u5DF2\u626B\u7801" : isExpired ? "\u5DF2\u8FC7\u671F" : "\u7B49\u5F85\u4E2D";
|
|
9026
|
+
const initialState = claimed ? "claimed" : isExpired ? "expired" : "waiting";
|
|
9029
9027
|
return `<!doctype html>
|
|
9030
|
-
<html lang="
|
|
9028
|
+
<html lang="en">
|
|
9031
9029
|
<head>
|
|
9032
9030
|
<meta charset="utf-8" />
|
|
9033
9031
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
@@ -9067,7 +9065,10 @@ async function renderPairingPage(options) {
|
|
|
9067
9065
|
.panel { width: min(1040px, 100%); border: 1px solid var(--line); border-radius: 28px; background: var(--panel); box-shadow: var(--shadow); backdrop-filter: blur(18px); overflow: hidden; }
|
|
9068
9066
|
.hero { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(320px, 390px); gap: 0; }
|
|
9069
9067
|
.copy { padding: 34px 34px 30px; border-right: 1px solid var(--line); }
|
|
9068
|
+
.header-row { display: flex; justify-content: space-between; align-items: center; }
|
|
9070
9069
|
.eyebrow { display: inline-flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); font-size: 13px; font-weight: 600; }
|
|
9070
|
+
.lang-btn { background: var(--accent-soft); color: var(--accent); border: none; border-radius: 999px; padding: 6px 14px; font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit; line-height: 1; }
|
|
9071
|
+
.lang-btn:hover { opacity: 0.75; }
|
|
9071
9072
|
h1 { margin: 18px 0 12px; font-size: clamp(34px, 4vw, 52px); line-height: 1.02; }
|
|
9072
9073
|
.subtitle { max-width: 42ch; margin: 0; color: var(--muted); font-size: 16px; line-height: 1.7; }
|
|
9073
9074
|
.meta-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 26px; }
|
|
@@ -9087,7 +9088,12 @@ async function renderPairingPage(options) {
|
|
|
9087
9088
|
.pill { display: inline-flex; align-items: center; justify-content: center; padding: 7px 11px; border-radius: 999px; background: rgba(11,132,87,0.12); color: var(--good); font-size: 12px; font-weight: 600; white-space: nowrap; }
|
|
9088
9089
|
.qr-frame { display: grid; place-items: center; padding: 18px; border-radius: 24px; background: linear-gradient(180deg, rgba(45,92,255,0.06), rgba(45,92,255,0)); border: 1px solid var(--line); }
|
|
9089
9090
|
.qr-frame img { width: min(100%, 300px); aspect-ratio: 1; display: block; border-radius: 18px; background: #fff; padding: 14px; }
|
|
9090
|
-
.
|
|
9091
|
+
.manual { margin-top: 16px; border: 1px solid var(--line); border-radius: 18px; overflow: hidden; }
|
|
9092
|
+
.manual-row { display: flex; flex-direction: column; gap: 2px; padding: 12px 16px; background: rgba(0,0,0,0.03); }
|
|
9093
|
+
.manual-row + .manual-row { border-top: 1px solid var(--line); }
|
|
9094
|
+
.manual-label { font-size: 11px; color: var(--muted); letter-spacing: 0.04em; }
|
|
9095
|
+
.manual-value { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 14px; word-break: break-all; user-select: all; }
|
|
9096
|
+
.manual-value.code { font-size: 20px; letter-spacing: 0.16em; text-align: center; }
|
|
9091
9097
|
.footer { display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap; padding-top: 16px; color: var(--muted); font-size: 13px; line-height: 1.55; }
|
|
9092
9098
|
@media (max-width: 920px) { .hero { grid-template-columns: 1fr; } .copy { border-right: none; border-bottom: 1px solid var(--line); } }
|
|
9093
9099
|
@media (max-width: 640px) { .copy, .qr { padding: 22px 18px; } .meta-grid { grid-template-columns: 1fr; } .status { align-items: flex-start; flex-direction: column; } .code { font-size: 18px; letter-spacing: 0.14em; } }
|
|
@@ -9098,12 +9104,15 @@ async function renderPairingPage(options) {
|
|
|
9098
9104
|
<section class="panel">
|
|
9099
9105
|
<div class="hero">
|
|
9100
9106
|
<div class="copy">
|
|
9101
|
-
<
|
|
9102
|
-
|
|
9103
|
-
|
|
9107
|
+
<div class="header-row">
|
|
9108
|
+
<span class="eyebrow">Hermes Link \xB7 ${escapeHtml(version)}</span>
|
|
9109
|
+
<button id="langToggle" class="lang-btn">\u4E2D\u6587</button>
|
|
9110
|
+
</div>
|
|
9111
|
+
<h1 data-i18n="h1">Complete Pairing in the App</h1>
|
|
9112
|
+
<p class="subtitle" data-i18n="subtitle">Scan the QR code or enter the connect token to link this device.</p>
|
|
9104
9113
|
<div class="meta-grid">
|
|
9105
9114
|
<div class="meta">
|
|
9106
|
-
<span class="meta-label"
|
|
9115
|
+
<span class="meta-label" data-i18n="metaLocalUrl">Local Address</span>
|
|
9107
9116
|
<div class="meta-value">${escapeHtml(currentUrl)}</div>
|
|
9108
9117
|
</div>
|
|
9109
9118
|
<div class="meta">
|
|
@@ -9111,52 +9120,57 @@ async function renderPairingPage(options) {
|
|
|
9111
9120
|
<div class="meta-value">${escapeHtml(linkId)}</div>
|
|
9112
9121
|
</div>
|
|
9113
9122
|
<div class="meta">
|
|
9114
|
-
<span class="meta-label"
|
|
9123
|
+
<span class="meta-label" data-i18n="metaConnectToken">Connect Token</span>
|
|
9115
9124
|
<div class="meta-value">${escapeHtml(session.code)}</div>
|
|
9116
9125
|
</div>
|
|
9117
9126
|
<div class="meta">
|
|
9118
|
-
<span class="meta-label"
|
|
9119
|
-
<div class="meta-value">${escapeHtml(formatDate(session.expires_at))}</div>
|
|
9127
|
+
<span class="meta-label" data-i18n="metaExpires">Expires</span>
|
|
9128
|
+
<div class="meta-value" id="expiresValue" data-iso="${escapeHtml(session.expires_at)}">${escapeHtml(formatDate(session.expires_at, "en-US"))}</div>
|
|
9120
9129
|
</div>
|
|
9121
9130
|
</div>
|
|
9122
9131
|
<div class="steps">
|
|
9123
9132
|
<div class="step">
|
|
9124
9133
|
<div class="step-badge">1</div>
|
|
9125
9134
|
<div>
|
|
9126
|
-
<p class="step-title"
|
|
9127
|
-
<p class="step-copy"
|
|
9135
|
+
<p class="step-title" data-i18n="step1Title">Open “Connect Hermes Link” in the App</p>
|
|
9136
|
+
<p class="step-copy" data-i18n="step1Copy">Find the “Connect Link” entry in the App, then scan the QR code or enter the token manually. The App will switch to this Link automatically after pairing.</p>
|
|
9128
9137
|
</div>
|
|
9129
9138
|
</div>
|
|
9130
9139
|
<div class="step">
|
|
9131
9140
|
<div class="step-badge">2</div>
|
|
9132
9141
|
<div>
|
|
9133
|
-
<p class="step-title"
|
|
9134
|
-
<p class="step-copy"
|
|
9142
|
+
<p class="step-title" data-i18n="step2Title">Scan the QR code, or enter the address and token manually</p>
|
|
9143
|
+
<p class="step-copy" data-i18n="step2Copy">If scanning is inconvenient, enter the address and connect token below in the App.</p>
|
|
9135
9144
|
</div>
|
|
9136
9145
|
</div>
|
|
9137
9146
|
<div class="step">
|
|
9138
9147
|
<div class="step-badge">3</div>
|
|
9139
9148
|
<div>
|
|
9140
|
-
<p class="step-title"
|
|
9141
|
-
<p class="step-copy" id="statusHint">${
|
|
9149
|
+
<p class="step-title" data-i18n="step3Title">This page will update automatically once pairing is complete</p>
|
|
9150
|
+
<p class="step-copy" id="statusHint">${initialState === "claimed" ? "Pairing complete. You can close this page." : initialState === "expired" ? "This QR code has expired. Please run hermeslink pair again." : "Open the App to scan, or copy the connect token for manual entry."}</p>
|
|
9142
9151
|
</div>
|
|
9143
9152
|
</div>
|
|
9144
9153
|
</div>
|
|
9145
|
-
<p class="hint"
|
|
9154
|
+
<p class="hint" data-i18n="hint">You can keep this page open to check pairing status later. If pairing has already succeeded, the page will not prompt for a re-scan.</p>
|
|
9146
9155
|
</div>
|
|
9147
9156
|
<div class="qr">
|
|
9148
9157
|
<div class="card">
|
|
9149
9158
|
<div class="status">
|
|
9150
|
-
<h2 class="status-title" id="statusTitle">${
|
|
9151
|
-
<span class="pill" id="statusPill">${
|
|
9159
|
+
<h2 class="status-title" id="statusTitle">${initialState === "claimed" ? "Pairing Complete" : initialState === "expired" ? "Pairing Expired" : "Waiting for App to Scan"}</h2>
|
|
9160
|
+
<span class="pill" id="statusPill">${initialState === "claimed" ? "Scanned" : initialState === "expired" ? "Expired" : "Waiting"}</span>
|
|
9152
9161
|
</div>
|
|
9153
9162
|
<div class="qr-frame">
|
|
9154
9163
|
<img src="${qrDataUri}" alt="Hermes Link pairing QR code" />
|
|
9155
9164
|
</div>
|
|
9156
|
-
<div class="
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9165
|
+
<div class="manual">
|
|
9166
|
+
<div class="manual-row">
|
|
9167
|
+
<span class="manual-label" data-i18n="manualAddrLabel">Address (pick any that works)</span>
|
|
9168
|
+
${session.preferred_urls.map((u) => `<span class="manual-value">${escapeHtml(u)}</span>`).join("\n ")}
|
|
9169
|
+
</div>
|
|
9170
|
+
<div class="manual-row">
|
|
9171
|
+
<span class="manual-label" data-i18n="manualTokenLabel">Connect Token</span>
|
|
9172
|
+
<span class="manual-value code">${escapeHtml(session.code)}</span>
|
|
9173
|
+
</div>
|
|
9160
9174
|
</div>
|
|
9161
9175
|
</div>
|
|
9162
9176
|
</div>
|
|
@@ -9167,26 +9181,104 @@ async function renderPairingPage(options) {
|
|
|
9167
9181
|
const sessionId = ${JSON.stringify(session.session_id)};
|
|
9168
9182
|
const expiresAtMs = ${Number.isFinite(expiresAtMs) ? String(expiresAtMs) : "Number.NaN"};
|
|
9169
9183
|
const initialClaimed = ${JSON.stringify(claimed)};
|
|
9170
|
-
|
|
9171
|
-
const
|
|
9184
|
+
|
|
9185
|
+
const T = {
|
|
9186
|
+
zh: {
|
|
9187
|
+
h1: '\u5728 App \u91CC\u5B8C\u6210\u8FD9\u6B21\u914D\u5BF9',
|
|
9188
|
+
subtitle: '\u626B\u7801\u6216\u624B\u52A8\u8F93\u5165\u914D\u5BF9\u7801\uFF0C\u5B8C\u6210 App \u4E0E\u672C\u673A\u7684\u8FDE\u63A5\u3002',
|
|
9189
|
+
metaLocalUrl: '\u672C\u5730\u5730\u5740',
|
|
9190
|
+
metaConnectToken: '\u914D\u5BF9\u7801',
|
|
9191
|
+
metaExpires: '\u8FC7\u671F\u65F6\u95F4',
|
|
9192
|
+
step1Title: '\u5728 App \u91CC\u6253\u5F00\u201C\u8FDE\u63A5 Hermes Link\u201D',
|
|
9193
|
+
step1Copy: '\u5728 App \u91CC\u627E\u5230\u201C\u8FDE\u63A5 Link\u201D\u5165\u53E3\uFF0C\u9009\u62E9\u626B\u7801\u6216\u624B\u52A8\u8F93\u5165\u914D\u5BF9\u7801\u3002\u914D\u5BF9\u6210\u529F\u540E\uFF0CApp \u4F1A\u81EA\u52A8\u5207\u5230\u8FD9\u53F0 Link\u3002',
|
|
9194
|
+
step2Title: '\u626B\u4E8C\u7EF4\u7801\uFF0C\u6216\u624B\u52A8\u586B\u5199\u5730\u5740\u548C\u914D\u5BF9\u7801',
|
|
9195
|
+
step2Copy: '\u5982\u679C\u626B\u7801\u4E0D\u65B9\u4FBF\uFF0C\u53EF\u5728 App \u91CC\u624B\u52A8\u8F93\u5165\u4E0B\u65B9\u7684\u5730\u5740\u548C\u914D\u5BF9\u7801\u5B8C\u6210\u8FDE\u63A5\u3002',
|
|
9196
|
+
step3Title: '\u914D\u5BF9\u6210\u529F\u540E\uFF0C\u8FD9\u4E2A\u9875\u9762\u4F1A\u81EA\u52A8\u53D8\u6210\u5DF2\u5B8C\u6210\u72B6\u6001',
|
|
9197
|
+
hint: '\u53EF\u5728\u7EC8\u7AEF\u7EE7\u7EED\u4FDD\u7559\u8FD9\u4E2A\u9875\u9762\uFF0C\u65B9\u4FBF\u7A0D\u540E\u6838\u5BF9\u72B6\u6001\uFF1B\u5982\u679C\u914D\u5BF9\u5DF2\u7ECF\u6210\u529F\uFF0C\u9875\u9762\u4E0D\u4F1A\u518D\u8981\u6C42\u91CD\u65B0\u626B\u7801\u3002',
|
|
9198
|
+
manualAddrLabel: '\u5730\u5740\uFF08\u4EFB\u9009\u4E00\u4E2A\u53EF\u7528\u7684\uFF09',
|
|
9199
|
+
manualTokenLabel: '\u914D\u5BF9\u7801',
|
|
9200
|
+
status_waiting: '\u7B49\u5F85 App \u626B\u7801',
|
|
9201
|
+
status_claimed: '\u5DF2\u5B8C\u6210\u914D\u5BF9',
|
|
9202
|
+
status_expired: '\u914D\u5BF9\u5DF2\u8FC7\u671F',
|
|
9203
|
+
pill_waiting: '\u7B49\u5F85\u4E2D',
|
|
9204
|
+
pill_claimed: '\u5DF2\u626B\u7801',
|
|
9205
|
+
pill_expired: '\u5DF2\u8FC7\u671F',
|
|
9206
|
+
hint_waiting: '\u6253\u5F00 App \u626B\u7801\uFF0C\u6216\u8005\u590D\u5236\u914D\u5BF9\u7801\u624B\u52A8\u8F93\u5165\u3002',
|
|
9207
|
+
hint_claimed: 'App \u5DF2\u5B8C\u6210\u914D\u5BF9\uFF0C\u8FD9\u4E2A\u9875\u9762\u53EF\u4EE5\u5173\u95ED\u3002',
|
|
9208
|
+
hint_expired: '\u8FD9\u6B21\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C hermeslink pair\u3002',
|
|
9209
|
+
expires_locale: 'zh-CN',
|
|
9210
|
+
langToggle: 'EN',
|
|
9211
|
+
},
|
|
9212
|
+
en: {
|
|
9213
|
+
h1: 'Complete Pairing in the App',
|
|
9214
|
+
subtitle: 'Scan the QR code or enter the connect token to link this device.',
|
|
9215
|
+
metaLocalUrl: 'Local Address',
|
|
9216
|
+
metaConnectToken: 'Connect Token',
|
|
9217
|
+
metaExpires: 'Expires',
|
|
9218
|
+
step1Title: 'Open \u201CConnect Hermes Link\u201D in the App',
|
|
9219
|
+
step1Copy: 'Find the \u201CConnect Link\u201D entry in the App, then scan the QR code or enter the token manually. The App will switch to this Link automatically after pairing.',
|
|
9220
|
+
step2Title: 'Scan the QR code, or enter the address and token manually',
|
|
9221
|
+
step2Copy: 'If scanning is inconvenient, enter the address and connect token below in the App.',
|
|
9222
|
+
step3Title: 'This page will update automatically once pairing is complete',
|
|
9223
|
+
hint: 'You can keep this page open to check pairing status later. If pairing has already succeeded, the page will not prompt for a re-scan.',
|
|
9224
|
+
manualAddrLabel: 'Address (pick any that works)',
|
|
9225
|
+
manualTokenLabel: 'Connect Token',
|
|
9226
|
+
status_waiting: 'Waiting for App to Scan',
|
|
9227
|
+
status_claimed: 'Pairing Complete',
|
|
9228
|
+
status_expired: 'Pairing Expired',
|
|
9229
|
+
pill_waiting: 'Waiting',
|
|
9230
|
+
pill_claimed: 'Scanned',
|
|
9231
|
+
pill_expired: 'Expired',
|
|
9232
|
+
hint_waiting: 'Open the App to scan, or copy the connect token for manual entry.',
|
|
9233
|
+
hint_claimed: 'Pairing complete. You can close this page.',
|
|
9234
|
+
hint_expired: 'This QR code has expired. Please run hermeslink pair again.',
|
|
9235
|
+
expires_locale: 'en-US',
|
|
9236
|
+
langToggle: '\u4E2D\u6587',
|
|
9237
|
+
},
|
|
9238
|
+
};
|
|
9239
|
+
|
|
9240
|
+
let lang = localStorage.getItem('hl-lang') || 'en';
|
|
9241
|
+
let state = ${JSON.stringify(initialState)};
|
|
9242
|
+
|
|
9243
|
+
const statusTitleEl = document.querySelector('#statusTitle');
|
|
9244
|
+
const statusPillEl = document.querySelector('#statusPill');
|
|
9172
9245
|
const statusHintEl = document.querySelector('#statusHint');
|
|
9173
|
-
|
|
9246
|
+
const langToggleEl = document.querySelector('#langToggle');
|
|
9247
|
+
const expiresEl = document.querySelector('#expiresValue');
|
|
9174
9248
|
|
|
9175
|
-
|
|
9249
|
+
function applyLang() {
|
|
9250
|
+
const s = T[lang];
|
|
9251
|
+
document.documentElement.lang = lang === 'en' ? 'en' : 'zh-CN';
|
|
9252
|
+
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
9253
|
+
const key = el.dataset.i18n;
|
|
9254
|
+
if (s[key] !== undefined) el.textContent = s[key];
|
|
9255
|
+
});
|
|
9256
|
+
statusTitleEl.textContent = s['status_' + state];
|
|
9257
|
+
statusPillEl.textContent = s['pill_' + state];
|
|
9258
|
+
statusHintEl.textContent = s['hint_' + state];
|
|
9259
|
+
if (expiresEl) {
|
|
9260
|
+
const iso = expiresEl.dataset.iso;
|
|
9261
|
+
const d = new Date(iso);
|
|
9262
|
+
expiresEl.textContent = isFinite(d.getTime())
|
|
9263
|
+
? d.toLocaleString(s.expires_locale, { hour12: lang === 'en' })
|
|
9264
|
+
: iso;
|
|
9265
|
+
}
|
|
9266
|
+
langToggleEl.textContent = s.langToggle;
|
|
9267
|
+
}
|
|
9176
9268
|
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
};
|
|
9269
|
+
langToggleEl.addEventListener('click', () => {
|
|
9270
|
+
lang = lang === 'zh' ? 'en' : 'zh';
|
|
9271
|
+
localStorage.setItem('hl-lang', lang);
|
|
9272
|
+
applyLang();
|
|
9273
|
+
});
|
|
9183
9274
|
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
};
|
|
9275
|
+
if (lang === 'zh') applyLang();
|
|
9276
|
+
|
|
9277
|
+
let refreshTimer = null;
|
|
9278
|
+
const stopPolling = () => { if (refreshTimer !== null) { clearInterval(refreshTimer); refreshTimer = null; } };
|
|
9279
|
+
|
|
9280
|
+
const markClaimed = () => { state = 'claimed'; applyLang(); stopPolling(); };
|
|
9281
|
+
const markExpired = () => { state = 'expired'; applyLang(); stopPolling(); };
|
|
9190
9282
|
|
|
9191
9283
|
const refresh = async () => {
|
|
9192
9284
|
if (Number.isFinite(expiresAtMs) && Date.now() >= expiresAtMs) { markExpired(); return; }
|
package/dist/cli/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
saveConfig,
|
|
22
22
|
startLinkService,
|
|
23
23
|
writeJsonFile
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-7IVSOP5F.js";
|
|
25
25
|
import "../chunk-NP3Y2NVF.js";
|
|
26
26
|
|
|
27
27
|
// src/cli/index.ts
|
|
@@ -259,7 +259,10 @@ async function runPairingPreflight(options) {
|
|
|
259
259
|
display_name: "Hermes Link",
|
|
260
260
|
session_id: sessionId,
|
|
261
261
|
code: token.token,
|
|
262
|
-
preferred_urls: preferredUrls
|
|
262
|
+
preferred_urls: preferredUrls,
|
|
263
|
+
lan_ips: routes?.lanIps ?? [],
|
|
264
|
+
public_ipv4s: routes?.publicIpv4s ?? [],
|
|
265
|
+
public_ipv6s: routes?.publicIpv6s ?? []
|
|
263
266
|
};
|
|
264
267
|
const pageUrl = buildLocalPairingPageUrl(preferredUrls[0] ?? `http://127.0.0.1:${options.config.port}`, sessionId, token.token);
|
|
265
268
|
if (options.openBrowser !== false) {
|
|
@@ -414,14 +417,16 @@ async function cmdPair(paths) {
|
|
|
414
417
|
const result = await runPairingPreflight({ identity, config, paths, openBrowser: false });
|
|
415
418
|
process.stdout.write("\n");
|
|
416
419
|
qrcode.generate(result.qrPayload, { small: true });
|
|
420
|
+
const pageUrls = result.preferredUrls.map((base) => buildLocalPairingPageUrl(base, result.sessionId));
|
|
421
|
+
const label = "Pairing page: ";
|
|
422
|
+
const indent = " ".repeat(label.length);
|
|
417
423
|
process.stdout.write(`
|
|
418
|
-
|
|
424
|
+
${label}${pageUrls.join(`
|
|
425
|
+
${indent}`)}
|
|
419
426
|
`);
|
|
420
427
|
process.stdout.write(`Session ID: ${result.sessionId}
|
|
421
428
|
`);
|
|
422
429
|
process.stdout.write(`Connect token: ${result.connectToken}
|
|
423
|
-
`);
|
|
424
|
-
process.stdout.write(`Preferred URLs: ${result.preferredUrls.join(", ")}
|
|
425
430
|
`);
|
|
426
431
|
process.stdout.write(`
|
|
427
432
|
App \u626B\u63CF\u4E8C\u7EF4\u7801\u540E\uFF0C\u8C03\u7528\u4EE5\u4E0B\u63A5\u53E3\u5B8C\u6210\u914D\u5BF9\uFF1A
|
package/dist/http/app.js
CHANGED
package/package.json
CHANGED