@bulolo/hermes-link 0.3.4 → 0.3.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/{chunk-ELQBIHDQ.js → chunk-EYXOHOAC.js} +211 -135
- package/dist/cli/index.js +3 -3
- package/dist/http/app.js +1 -1
- package/package.json +2 -2
|
@@ -8885,10 +8885,29 @@ async function startLinkService(options) {
|
|
|
8885
8885
|
});
|
|
8886
8886
|
const rootRouter = new Router13();
|
|
8887
8887
|
rootRouter.get("/pair", async (ctx) => {
|
|
8888
|
-
const
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8888
|
+
const sessionId = typeof ctx.query.session_id === "string" ? ctx.query.session_id : "";
|
|
8889
|
+
if (!sessionId) {
|
|
8890
|
+
ctx.status = 400;
|
|
8891
|
+
ctx.type = "text/plain";
|
|
8892
|
+
ctx.body = "Missing session_id";
|
|
8893
|
+
return;
|
|
8894
|
+
}
|
|
8895
|
+
const session = await readPairingSession(sessionId, paths);
|
|
8896
|
+
if (!session) {
|
|
8897
|
+
ctx.status = 404;
|
|
8898
|
+
ctx.type = "text/plain";
|
|
8899
|
+
ctx.body = "Pairing session not found";
|
|
8900
|
+
return;
|
|
8901
|
+
}
|
|
8902
|
+
const claimed = await isPairingSessionClaimed(sessionId, paths);
|
|
8903
|
+
ctx.set("content-type", "text/html; charset=utf-8");
|
|
8904
|
+
ctx.set("cache-control", "no-store");
|
|
8905
|
+
ctx.body = await renderPairingPage({
|
|
8906
|
+
session,
|
|
8907
|
+
claimed,
|
|
8908
|
+
version: LINK_VERSION,
|
|
8909
|
+
linkId: identity.link_id ?? session.link_id
|
|
8910
|
+
});
|
|
8892
8911
|
});
|
|
8893
8912
|
rootRouter.get("/api/v1/status", async (ctx) => {
|
|
8894
8913
|
await authenticateRequest(ctx, paths);
|
|
@@ -8980,151 +8999,208 @@ async function startLinkService(options) {
|
|
|
8980
8999
|
};
|
|
8981
9000
|
return { app, server, stop };
|
|
8982
9001
|
}
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
9002
|
+
function escapeHtml(s) {
|
|
9003
|
+
return s.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """);
|
|
9004
|
+
}
|
|
9005
|
+
function formatDate(iso) {
|
|
9006
|
+
const d = new Date(iso);
|
|
9007
|
+
if (!Number.isFinite(d.getTime())) return iso;
|
|
9008
|
+
return d.toLocaleString("zh-CN", { hour12: false });
|
|
9009
|
+
}
|
|
9010
|
+
async function renderPairingPage(options) {
|
|
9011
|
+
const { session, claimed, version, linkId } = options;
|
|
9012
|
+
const isExpired = !claimed && isPairingSessionExpired(session);
|
|
9013
|
+
const qrPayload = JSON.stringify({
|
|
8986
9014
|
kind: "hermes_link_pairing",
|
|
8987
9015
|
version: 1,
|
|
8988
|
-
link_id:
|
|
8989
|
-
display_name:
|
|
8990
|
-
session_id:
|
|
8991
|
-
code:
|
|
8992
|
-
preferred_urls:
|
|
8993
|
-
})
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
qrHtml = `<div class="qr">${qrSvg}</div>`;
|
|
9004
|
-
} catch {
|
|
9005
|
-
qrHtml = "";
|
|
9006
|
-
}
|
|
9007
|
-
}
|
|
9008
|
-
const baseUrl = `http://127.0.0.1:${port}`;
|
|
9009
|
-
return `<!DOCTYPE html>
|
|
9010
|
-
<html lang="en">
|
|
9016
|
+
link_id: session.link_id,
|
|
9017
|
+
display_name: session.display_name,
|
|
9018
|
+
session_id: session.session_id,
|
|
9019
|
+
code: session.code,
|
|
9020
|
+
preferred_urls: session.preferred_urls
|
|
9021
|
+
});
|
|
9022
|
+
const qrSvg = await QRCode.toString(qrPayload, { type: "svg", margin: 1, width: 320, errorCorrectionLevel: "M" });
|
|
9023
|
+
const qrDataUri = `data:image/svg+xml;base64,${Buffer.from(qrSvg).toString("base64")}`;
|
|
9024
|
+
const currentUrl = session.local_api_url.replace(/\/+$/u, "");
|
|
9025
|
+
const expiresAtMs = Date.parse(session.expires_at);
|
|
9026
|
+
const statusLabel = claimed ? "\u5DF2\u5B8C\u6210\u914D\u5BF9" : isExpired ? "\u914D\u5BF9\u5DF2\u8FC7\u671F" : "\u7B49\u5F85 App \u626B\u7801";
|
|
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";
|
|
9029
|
+
return `<!doctype html>
|
|
9030
|
+
<html lang="zh-CN">
|
|
9011
9031
|
<head>
|
|
9012
|
-
<meta charset="
|
|
9013
|
-
<meta name="viewport" content="width=device-width, initial-scale=1
|
|
9014
|
-
<
|
|
9032
|
+
<meta charset="utf-8" />
|
|
9033
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9034
|
+
<meta name="color-scheme" content="light dark" />
|
|
9035
|
+
<title>Hermes Link Pairing</title>
|
|
9015
9036
|
<style>
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9037
|
+
:root {
|
|
9038
|
+
color-scheme: light dark;
|
|
9039
|
+
--bg: #f4f5f7;
|
|
9040
|
+
--panel: rgba(255,255,255,0.78);
|
|
9041
|
+
--panel-strong: rgba(255,255,255,0.94);
|
|
9042
|
+
--text: #151922;
|
|
9043
|
+
--muted: #5f6673;
|
|
9044
|
+
--line: rgba(21,25,34,0.12);
|
|
9045
|
+
--accent: #2d5cff;
|
|
9046
|
+
--accent-soft: rgba(45,92,255,0.12);
|
|
9047
|
+
--good: #0b8457;
|
|
9048
|
+
--shadow: 0 24px 90px rgba(18,24,38,0.12);
|
|
9049
|
+
}
|
|
9050
|
+
@media (prefers-color-scheme: dark) {
|
|
9051
|
+
:root {
|
|
9052
|
+
--bg: #0c1017;
|
|
9053
|
+
--panel: rgba(16,20,28,0.78);
|
|
9054
|
+
--panel-strong: rgba(16,20,28,0.94);
|
|
9055
|
+
--text: #eef2f8;
|
|
9056
|
+
--muted: #9ba4b3;
|
|
9057
|
+
--line: rgba(255,255,255,0.12);
|
|
9058
|
+
--accent: #8ab4ff;
|
|
9059
|
+
--accent-soft: rgba(138,180,255,0.12);
|
|
9060
|
+
--good: #67d7a7;
|
|
9061
|
+
--shadow: 0 24px 90px rgba(0,0,0,0.45);
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9064
|
+
* { box-sizing: border-box; }
|
|
9065
|
+
body { margin: 0; min-height: 100vh; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: var(--text); background: linear-gradient(180deg, var(--bg) 0%, color-mix(in srgb, var(--bg) 88%, var(--accent) 12%) 100%); }
|
|
9066
|
+
.shell { min-height: 100vh; display: grid; place-items: center; padding: 28px 18px; }
|
|
9067
|
+
.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
|
+
.hero { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(320px, 390px); gap: 0; }
|
|
9069
|
+
.copy { padding: 34px 34px 30px; border-right: 1px solid var(--line); }
|
|
9070
|
+
.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; }
|
|
9071
|
+
h1 { margin: 18px 0 12px; font-size: clamp(34px, 4vw, 52px); line-height: 1.02; }
|
|
9072
|
+
.subtitle { max-width: 42ch; margin: 0; color: var(--muted); font-size: 16px; line-height: 1.7; }
|
|
9073
|
+
.meta-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 26px; }
|
|
9074
|
+
.meta { padding: 16px 16px 15px; border-radius: 18px; background: var(--panel-strong); border: 1px solid var(--line); }
|
|
9075
|
+
.meta-label { display: block; color: var(--muted); font-size: 12px; line-height: 1.4; margin-bottom: 8px; }
|
|
9076
|
+
.meta-value { font-size: 15px; line-height: 1.5; word-break: break-word; }
|
|
9077
|
+
.steps { display: grid; gap: 10px; margin-top: 18px; }
|
|
9078
|
+
.step { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border: 1px solid var(--line); border-radius: 18px; background: var(--panel-strong); }
|
|
9079
|
+
.step-badge { flex: none; width: 26px; height: 26px; border-radius: 999px; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent); font-size: 13px; font-weight: 600; }
|
|
9080
|
+
.step-title { font-size: 14px; line-height: 1.45; margin: 0; font-weight: 600; }
|
|
9081
|
+
.step-copy { margin: 3px 0 0; color: var(--muted); font-size: 13px; line-height: 1.55; }
|
|
9082
|
+
.hint { margin: 10px 0 0; color: var(--muted); font-size: 13px; line-height: 1.55; }
|
|
9083
|
+
.qr { padding: 26px 26px 30px; background: linear-gradient(180deg, rgba(255,255,255,0.16), rgba(255,255,255,0)); }
|
|
9084
|
+
.card { border: 1px solid var(--line); border-radius: 24px; background: var(--panel-strong); padding: 20px; }
|
|
9085
|
+
.status { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; }
|
|
9086
|
+
.status-title { margin: 0; font-size: 18px; line-height: 1.3; }
|
|
9087
|
+
.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
|
+
.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
|
+
.qr-frame img { width: min(100%, 300px); aspect-ratio: 1; display: block; border-radius: 18px; background: #fff; padding: 14px; }
|
|
9090
|
+
.code { margin-top: 16px; padding: 14px 16px; border-radius: 18px; border: 1px solid var(--line); background: rgba(0,0,0,0.03); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 22px; letter-spacing: 0.18em; text-align: center; user-select: all; }
|
|
9091
|
+
.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
|
+
@media (max-width: 920px) { .hero { grid-template-columns: 1fr; } .copy { border-right: none; border-bottom: 1px solid var(--line); } }
|
|
9093
|
+
@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; } }
|
|
9036
9094
|
</style>
|
|
9037
9095
|
</head>
|
|
9038
9096
|
<body>
|
|
9039
|
-
<
|
|
9040
|
-
<
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9097
|
+
<main class="shell">
|
|
9098
|
+
<section class="panel">
|
|
9099
|
+
<div class="hero">
|
|
9100
|
+
<div class="copy">
|
|
9101
|
+
<span class="eyebrow">Hermes Link \xB7 ${escapeHtml(version)}</span>
|
|
9102
|
+
<h1>\u5728 App \u91CC\u5B8C\u6210\u8FD9\u6B21\u914D\u5BF9</h1>
|
|
9103
|
+
<p class="subtitle">\u8FD9\u4E0D\u662F\u4E00\u5F20\u5B64\u96F6\u96F6\u7684\u4E8C\u7EF4\u7801\u3002\u5B83\u540C\u65F6\u7ED9\u4F60\u914D\u5BF9\u72B6\u6001\u3001\u624B\u52A8\u7801\u3001\u6709\u6548\u671F\u548C\u5F53\u524D\u672C\u5730\u5730\u5740\uFF0C\u65B9\u4FBF Windows \u4F20\u7EDF\u63A7\u5236\u53F0\u3001\u8FDC\u7A0B\u7EC8\u7AEF\u548C\u6D4F\u89C8\u5668\u6253\u5F00\u65F6\u90FD\u80FD\u987A\u5229\u5B8C\u6210\u3002</p>
|
|
9104
|
+
<div class="meta-grid">
|
|
9105
|
+
<div class="meta">
|
|
9106
|
+
<span class="meta-label">\u672C\u5730\u5730\u5740</span>
|
|
9107
|
+
<div class="meta-value">${escapeHtml(currentUrl)}</div>
|
|
9108
|
+
</div>
|
|
9109
|
+
<div class="meta">
|
|
9110
|
+
<span class="meta-label">Link ID</span>
|
|
9111
|
+
<div class="meta-value">${escapeHtml(linkId)}</div>
|
|
9112
|
+
</div>
|
|
9113
|
+
<div class="meta">
|
|
9114
|
+
<span class="meta-label">\u914D\u5BF9\u7801</span>
|
|
9115
|
+
<div class="meta-value">${escapeHtml(session.code)}</div>
|
|
9116
|
+
</div>
|
|
9117
|
+
<div class="meta">
|
|
9118
|
+
<span class="meta-label">\u8FC7\u671F\u65F6\u95F4</span>
|
|
9119
|
+
<div class="meta-value">${escapeHtml(formatDate(session.expires_at))}</div>
|
|
9120
|
+
</div>
|
|
9121
|
+
</div>
|
|
9122
|
+
<div class="steps">
|
|
9123
|
+
<div class="step">
|
|
9124
|
+
<div class="step-badge">1</div>
|
|
9125
|
+
<div>
|
|
9126
|
+
<p class="step-title">\u5728 App \u91CC\u6253\u5F00"\u8FDE\u63A5 Hermes Link"</p>
|
|
9127
|
+
<p class="step-copy">\u5148\u767B\u5F55 HermesPilot \u8D26\u53F7\uFF0C\u518D\u8D70\u626B\u7801\u6216\u624B\u52A8\u7801\u3002\u914D\u5BF9\u6210\u529F\u540E\uFF0CApp \u4F1A\u81EA\u52A8\u5207\u5230\u8FD9\u53F0 Link\u3002</p>
|
|
9128
|
+
</div>
|
|
9129
|
+
</div>
|
|
9130
|
+
<div class="step">
|
|
9131
|
+
<div class="step-badge">2</div>
|
|
9132
|
+
<div>
|
|
9133
|
+
<p class="step-title">\u626B\u4E8C\u7EF4\u7801\uFF0C\u6216\u76F4\u63A5\u8F93\u5165\u914D\u5BF9\u7801</p>
|
|
9134
|
+
<p class="step-copy">\u5982\u679C\u626B\u7801\u4E0D\u65B9\u4FBF\uFF0CApp \u91CC\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165\u8FD9\u4E2A\u9875\u9762\u663E\u793A\u7684\u914D\u5BF9\u7801\u3002</p>
|
|
9135
|
+
</div>
|
|
9136
|
+
</div>
|
|
9137
|
+
<div class="step">
|
|
9138
|
+
<div class="step-badge">3</div>
|
|
9139
|
+
<div>
|
|
9140
|
+
<p class="step-title">\u914D\u5BF9\u6210\u529F\u540E\uFF0C\u8FD9\u4E2A\u9875\u9762\u4F1A\u81EA\u52A8\u53D8\u6210\u5DF2\u5B8C\u6210\u72B6\u6001</p>
|
|
9141
|
+
<p class="step-copy" id="statusHint">${escapeHtml(statusHint)}</p>
|
|
9142
|
+
</div>
|
|
9143
|
+
</div>
|
|
9144
|
+
</div>
|
|
9145
|
+
<p class="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</p>
|
|
9146
|
+
</div>
|
|
9147
|
+
<div class="qr">
|
|
9148
|
+
<div class="card">
|
|
9149
|
+
<div class="status">
|
|
9150
|
+
<h2 class="status-title" id="statusTitle">${escapeHtml(statusLabel)}</h2>
|
|
9151
|
+
<span class="pill" id="statusPill">${escapeHtml(statusPillLabel)}</span>
|
|
9152
|
+
</div>
|
|
9153
|
+
<div class="qr-frame">
|
|
9154
|
+
<img src="${qrDataUri}" alt="Hermes Link pairing QR code" />
|
|
9155
|
+
</div>
|
|
9156
|
+
<div class="code">${escapeHtml(session.code)}</div>
|
|
9157
|
+
<div class="footer">
|
|
9158
|
+
<span>Local API: ${escapeHtml(currentUrl)}</span>
|
|
9159
|
+
<span>Relay / Server \u4F1A\u540C\u65F6\u51C6\u5907\u53EF\u7528\u8DEF\u7531</span>
|
|
9160
|
+
</div>
|
|
9161
|
+
</div>
|
|
9162
|
+
</div>
|
|
9163
|
+
</div>
|
|
9164
|
+
</section>
|
|
9165
|
+
</main>
|
|
9166
|
+
<script>
|
|
9167
|
+
const sessionId = ${JSON.stringify(session.session_id)};
|
|
9168
|
+
const expiresAtMs = ${Number.isFinite(expiresAtMs) ? String(expiresAtMs) : "Number.NaN"};
|
|
9169
|
+
const initialClaimed = ${JSON.stringify(claimed)};
|
|
9170
|
+
const statusTitle = document.querySelector('#statusTitle');
|
|
9171
|
+
const statusPill = document.querySelector('#statusPill');
|
|
9172
|
+
const statusHintEl = document.querySelector('#statusHint');
|
|
9173
|
+
let refreshTimer = null;
|
|
9059
9174
|
|
|
9060
|
-
|
|
9061
|
-
<div class="label">\u914D\u5BF9\u63A5\u53E3\uFF08App \u8C03\u7528\uFF09</div>
|
|
9062
|
-
<div class="mono">POST ${baseUrl}/api/v1/pairing/claim</div>
|
|
9063
|
-
</div>
|
|
9175
|
+
const stopPolling = () => { if (refreshTimer !== null) { clearInterval(refreshTimer); refreshTimer = null; } };
|
|
9064
9176
|
|
|
9065
|
-
|
|
9177
|
+
const markClaimed = () => {
|
|
9178
|
+
statusTitle.textContent = '\u5DF2\u5B8C\u6210\u914D\u5BF9';
|
|
9179
|
+
statusPill.textContent = '\u5DF2\u626B\u7801';
|
|
9180
|
+
statusHintEl.textContent = 'App \u5DF2\u5B8C\u6210\u914D\u5BF9\uFF0C\u8FD9\u4E2A\u9875\u9762\u53EF\u4EE5\u5173\u95ED\u3002';
|
|
9181
|
+
stopPolling();
|
|
9182
|
+
};
|
|
9066
9183
|
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9184
|
+
const markExpired = () => {
|
|
9185
|
+
statusTitle.textContent = '\u914D\u5BF9\u5DF2\u8FC7\u671F';
|
|
9186
|
+
statusPill.textContent = '\u5DF2\u8FC7\u671F';
|
|
9187
|
+
statusHintEl.textContent = '\u8FD9\u6B21\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C hermeslink pair\u3002';
|
|
9188
|
+
stopPolling();
|
|
9189
|
+
};
|
|
9072
9190
|
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
let pollTimer = setInterval(async () => {
|
|
9191
|
+
const refresh = async () => {
|
|
9192
|
+
if (Number.isFinite(expiresAtMs) && Date.now() >= expiresAtMs) { markExpired(); return; }
|
|
9076
9193
|
try {
|
|
9077
|
-
const
|
|
9078
|
-
|
|
9079
|
-
if (
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
}
|
|
9194
|
+
const response = await fetch('/api/v1/pairing/session?session_id=' + encodeURIComponent(sessionId), { headers: { accept: 'application/json' } });
|
|
9195
|
+
if (response.status === 404) { markExpired(); return; }
|
|
9196
|
+
if (!response.ok) return;
|
|
9197
|
+
const payload = await response.json();
|
|
9198
|
+
if (payload?.session?.claimed) markClaimed();
|
|
9083
9199
|
} catch {}
|
|
9084
|
-
}
|
|
9085
|
-
` : ""}
|
|
9086
|
-
|
|
9087
|
-
async function pairBrowser() {
|
|
9088
|
-
const btn = document.getElementById('btn');
|
|
9089
|
-
btn.disabled = true;
|
|
9090
|
-
btn.textContent = '\u914D\u5BF9\u4E2D...';
|
|
9091
|
-
try {
|
|
9092
|
-
const res = await fetch('/api/v1/auth/device-session', {
|
|
9093
|
-
method: 'POST',
|
|
9094
|
-
headers: { 'Authorization': 'Bearer ${connectToken}', 'Content-Type': 'application/json' },
|
|
9095
|
-
body: JSON.stringify({ device_label: navigator.userAgent.slice(0, 64), device_platform: 'web' })
|
|
9096
|
-
});
|
|
9097
|
-
const data = await res.json();
|
|
9098
|
-
if (res.ok && data.access_token) {
|
|
9099
|
-
btn.textContent = '\u5DF2\u914D\u5BF9';
|
|
9100
|
-
showStatus('success', '\u914D\u5BF9\u6210\u529F\uFF01');
|
|
9101
|
-
const results = document.getElementById('results');
|
|
9102
|
-
results.innerHTML = \`
|
|
9103
|
-
<div class="result-row"><span class="tag">access_token \xB7 2h</span><div class="mono" onclick="copyText(this)">\${data.access_token.token}</div></div>
|
|
9104
|
-
<div class="result-row" style="margin-top:0.5rem"><span class="tag">refresh_token \xB7 90days</span><div class="mono" onclick="copyText(this)">\${data.refresh_token.token}</div></div>
|
|
9105
|
-
\`;
|
|
9106
|
-
} else {
|
|
9107
|
-
throw new Error(data.error?.message || JSON.stringify(data));
|
|
9108
|
-
}
|
|
9109
|
-
} catch (e) {
|
|
9110
|
-
btn.disabled = false;
|
|
9111
|
-
btn.textContent = '\u5728\u6B64\u8BBE\u5907\u4E0A\u914D\u5BF9';
|
|
9112
|
-
showStatus('error', '\u914D\u5BF9\u5931\u8D25: ' + e.message);
|
|
9113
|
-
}
|
|
9114
|
-
}
|
|
9115
|
-
|
|
9116
|
-
function showStatus(type, msg) {
|
|
9117
|
-
const el = document.getElementById('status');
|
|
9118
|
-
el.className = 'status ' + type;
|
|
9119
|
-
el.textContent = msg;
|
|
9120
|
-
}
|
|
9200
|
+
};
|
|
9121
9201
|
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
const orig = el.style.borderColor;
|
|
9125
|
-
el.style.borderColor = '#10b981';
|
|
9126
|
-
setTimeout(() => el.style.borderColor = orig, 800);
|
|
9127
|
-
}).catch(() => {});
|
|
9202
|
+
if (!initialClaimed) {
|
|
9203
|
+
refreshTimer = setInterval(refresh, 2000);
|
|
9128
9204
|
}
|
|
9129
9205
|
</script>
|
|
9130
9206
|
</body>
|
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-EYXOHOAC.js";
|
|
25
25
|
import "../chunk-NP3Y2NVF.js";
|
|
26
26
|
|
|
27
27
|
// src/cli/index.ts
|
|
@@ -273,8 +273,8 @@ async function runPairingPreflight(options) {
|
|
|
273
273
|
preferredUrls
|
|
274
274
|
};
|
|
275
275
|
}
|
|
276
|
-
function buildLocalPairingPageUrl(baseUrl, sessionId,
|
|
277
|
-
const qs = new URLSearchParams({ session_id: sessionId
|
|
276
|
+
function buildLocalPairingPageUrl(baseUrl, sessionId, _connectToken) {
|
|
277
|
+
const qs = new URLSearchParams({ session_id: sessionId });
|
|
278
278
|
return `${baseUrl}/pair?${qs.toString()}`;
|
|
279
279
|
}
|
|
280
280
|
|
package/dist/http/app.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bulolo/hermes-link",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Provides full client API, multi-device auth and conversation management for Hermes Agent, with LAN and internet connectivity.",
|
|
5
5
|
"author": "Bulolo",
|
|
6
6
|
"license": "MIT",
|
|
@@ -75,4 +75,4 @@
|
|
|
75
75
|
"typescript": "^5.7.2",
|
|
76
76
|
"vitest": "^2.1.8"
|
|
77
77
|
}
|
|
78
|
-
}
|
|
78
|
+
}
|