dsh-lark-agent 0.3.0 → 0.3.2
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 +2 -0
- package/dist/client/ConnectionCard.d.ts +6 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client.cjs +77 -7
- package/dist/onboarding/host.d.ts +6 -1
- package/dist/onboarding/host.js +30 -3
- package/dist/onboarding/protocol.d.ts +5 -0
- package/dist/onboarding/updates.d.ts +33 -0
- package/dist/onboarding/updates.js +83 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,8 @@ Then restart `dsh web`.
|
|
|
53
53
|
|
|
54
54
|
### Update
|
|
55
55
|
|
|
56
|
+
When a new version has been out for a day, the Feishu settings page shows a notice with these steps (in the desktop app it also opens the plugin list for you). Until then the desktop app and `dsh plugin` still install the previous version, because pnpm 11 skips releases less than 24 hours old. If an install picks an older version that your DeepSeek Harness rejects as incompatible, see [Troubleshooting](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#排障).
|
|
57
|
+
|
|
56
58
|
1. On the Feishu settings page, click **暂停连接** (pause) and back up as described in [Upgrade](https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#升级).
|
|
57
59
|
2. Desktop app: the Add plugin dialog says third-party plugins must be uninstalled before a new version is installed, so uninstall `dsh-lark-agent` on the Plugins page and add it again (this in-app upgrade hasn't been tested end to end yet; an earlier source build is listed as `dsh-larkbot`). CLI: run `dsh plugin --profile web update dsh-lark-agent@latest`, then restart `dsh web`.
|
|
58
60
|
3. Click **恢复连接** (resume) and check that the bot replies.
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ConnectionApi } from './api.js';
|
|
2
|
+
/** `app` is the desktop App; `openPlugins` switches to its plugin list and reports whether it could. */
|
|
2
3
|
export interface ConnectionCardProps {
|
|
3
4
|
api: ConnectionApi;
|
|
5
|
+
host?: {
|
|
6
|
+
app: boolean;
|
|
7
|
+
openPlugins?: () => boolean;
|
|
8
|
+
};
|
|
4
9
|
}
|
|
5
10
|
/** The host is authoritative for every transition, including readiness and identity. */
|
|
6
|
-
export declare function ConnectionCard({ api }: ConnectionCardProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function ConnectionCard({ api, host }: ConnectionCardProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface ClientContext {
|
|
|
20
20
|
inject: () => ConnectionCardProps;
|
|
21
21
|
}, component: ComponentType<ConnectionCardProps>): () => void;
|
|
22
22
|
};
|
|
23
|
+
/** Optional services by name, such as the App's plugin page navigation. */
|
|
24
|
+
get?(name: string): unknown;
|
|
23
25
|
}
|
|
24
26
|
export declare const inject: string[];
|
|
25
27
|
/** Add a dedicated settings tab; both host versions own this public slot. */
|
package/dist/client.cjs
CHANGED
|
@@ -111,6 +111,15 @@ function parseSnapshot(value) {
|
|
|
111
111
|
snapshot.documentSetup.qr = { url: officialUrl(qr.url), image, expiresAt: timestamp(qr.expiresAt) };
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
if (data.update !== void 0) {
|
|
115
|
+
const update = object(data.update);
|
|
116
|
+
const version = (value2) => {
|
|
117
|
+
const raw = text(value2, 64);
|
|
118
|
+
if (!/^\d{1,9}\.\d{1,9}\.\d{1,9}(?:-[0-9A-Za-z.-]{1,40})?$/.test(raw)) throw new Error("invalid version");
|
|
119
|
+
return raw;
|
|
120
|
+
};
|
|
121
|
+
snapshot.update = { current: version(update.current), latest: version(update.latest) };
|
|
122
|
+
}
|
|
114
123
|
return snapshot;
|
|
115
124
|
}
|
|
116
125
|
function parseProfiles(value) {
|
|
@@ -275,6 +284,8 @@ var deepseek_harness_icon_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEU
|
|
|
275
284
|
|
|
276
285
|
// src/client/ConnectionCard.tsx
|
|
277
286
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
287
|
+
var UPGRADE_COMMAND = "dsh plugin --profile web update dsh-lark-agent@latest";
|
|
288
|
+
var UPGRADE_GUIDE = "https://github.com/Nirvana-Jie/dsh-lark-agent/blob/main/DEVELOPMENT.md#\u5347\u7EA7";
|
|
278
289
|
var phaseLabels = {
|
|
279
290
|
idle: "\u672A\u8FDE\u63A5",
|
|
280
291
|
creating: "\u6B63\u5728\u521B\u5EFA",
|
|
@@ -319,7 +330,54 @@ function Steps({ active }) {
|
|
|
319
330
|
label
|
|
320
331
|
] }, label)) });
|
|
321
332
|
}
|
|
322
|
-
function
|
|
333
|
+
function UpdateNotice({ update, host, phase }) {
|
|
334
|
+
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
335
|
+
const [status, setStatus] = (0, import_react2.useState)("");
|
|
336
|
+
const app = host?.app ?? false;
|
|
337
|
+
const settled = phase === "connected" || phase === "paused";
|
|
338
|
+
const copy = async () => {
|
|
339
|
+
try {
|
|
340
|
+
await navigator.clipboard.writeText(UPGRADE_COMMAND);
|
|
341
|
+
setStatus("\u5DF2\u590D\u5236\u5347\u7EA7\u547D\u4EE4");
|
|
342
|
+
} catch {
|
|
343
|
+
setStatus("\u8BF7\u9009\u4E2D\u4E0A\u65B9\u547D\u4EE4\u624B\u52A8\u590D\u5236");
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-update", children: [
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-update-row", children: [
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
|
|
350
|
+
"\u98DE\u4E66\u63D2\u4EF6\u6709\u65B0\u7248\u672C ",
|
|
351
|
+
update.latest
|
|
352
|
+
] }),
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
|
|
354
|
+
"\u5F53\u524D ",
|
|
355
|
+
update.current,
|
|
356
|
+
"\uFF0C\u5347\u7EA7\u540E\u4FDD\u7559\u73B0\u6709\u8FDE\u63A5\u548C\u5BF9\u8BDD\u3002"
|
|
357
|
+
] })
|
|
358
|
+
] }),
|
|
359
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", "aria-expanded": open, onClick: () => setOpen(!open), children: open ? "\u6536\u8D77" : "\u5347\u7EA7" })
|
|
360
|
+
] }),
|
|
361
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
362
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ol", { className: "lark-instructions", children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: phase === "paused" ? "\u8FDE\u63A5\u5DF2\u6682\u505C\uFF0C\u53EF\u4EE5\u76F4\u63A5\u5347\u7EA7\u3002" : settled ? "\u5148\u70B9\u4E0B\u65B9\u300C\u6682\u505C\u8FDE\u63A5\u300D\u3002" : "\u5148\u5B8C\u6210\u6216\u53D6\u6D88\u5F53\u524D\u7684\u8FDE\u63A5\u6B65\u9AA4\u3002" }),
|
|
364
|
+
app ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u5728\u300C\u63D2\u4EF6\u5217\u8868\u300D\u5378\u8F7D dsh-lark-agent\uFF0C\u518D\u70B9\u300C\u6DFB\u52A0\u63D2\u4EF6\u300D\u8F93\u5165 dsh-lark-agent \u5B89\u88C5\u65B0\u7248\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
365
|
+
"\u5728\u7EC8\u7AEF\u8FD0\u884C\u4E0B\u9762\u7684\u547D\u4EE4\uFF0C\u7136\u540E\u91CD\u542F dsh web\uFF1A",
|
|
366
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "lark-command", children: UPGRADE_COMMAND })
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: settled ? "\u56DE\u5230\u8FD9\u91CC\u70B9\u300C\u6062\u590D\u8FDE\u63A5\u300D\uFF0C\u5728\u98DE\u4E66\u53D1\u4E00\u53E5\u8BDD\u786E\u8BA4\u80FD\u56DE\u590D\u3002" : "\u5347\u7EA7\u540E\u56DE\u5230\u8FD9\u91CC\u7EE7\u7EED\u3002" })
|
|
369
|
+
] }),
|
|
370
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-actions", children: [
|
|
371
|
+
app ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", onClick: () => {
|
|
372
|
+
if (!host?.openPlugins?.()) setStatus("\u8BF7\u5207\u5230\u4E0A\u65B9\u7684\u300C\u63D2\u4EF6\u5217\u8868\u300D\u7EE7\u7EED\u3002");
|
|
373
|
+
}, children: "\u6253\u5F00\u63D2\u4EF6\u5217\u8868" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-secondary", onClick: () => void copy(), children: "\u590D\u5236\u5347\u7EA7\u547D\u4EE4" }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(External, { className: "lark-text-button", url: UPGRADE_GUIDE, children: "\u5347\u7EA7\u524D\u7684\u5907\u4EFD\u8BF4\u660E" })
|
|
375
|
+
] }),
|
|
376
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lark-copy-status", role: "status", children: status })
|
|
377
|
+
] })
|
|
378
|
+
] });
|
|
379
|
+
}
|
|
380
|
+
function ConnectionCard({ api, host }) {
|
|
323
381
|
const { snapshot, error, pending, profiles, refresh, mutate, loadProfiles } = useConnection(api);
|
|
324
382
|
const [chooseExisting, setChooseExisting] = (0, import_react2.useState)(false);
|
|
325
383
|
const [selected, setSelected] = (0, import_react2.useState)("");
|
|
@@ -506,7 +564,7 @@ function ConnectionCard({ api }) {
|
|
|
506
564
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lark-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-quiet", disabled, onClick: cancel, children: "\u53D6\u6D88\u8FDE\u63A5" }) })
|
|
507
565
|
] });
|
|
508
566
|
if (phase === "connected" || phase === "paused") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
509
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Steps, { active:
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Steps, { active: 3 }),
|
|
510
568
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrandLink, { connected: phase === "connected" }),
|
|
511
569
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "lark-intro-title", children: phase === "paused" ? "\u5DF2\u6682\u505C\u98DE\u4E66\u8FDE\u63A5" : "\u53EF\u4EE5\u5728\u98DE\u4E66\u627E\u6211\u4E86" }),
|
|
512
570
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "lark-copy lark-centered", children: phase === "paused" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
@@ -598,6 +656,7 @@ function ConnectionCard({ api }) {
|
|
|
598
656
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error }),
|
|
599
657
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "lark-text-button", disabled, onClick: refresh, children: "\u91CD\u8BD5" })
|
|
600
658
|
] }),
|
|
659
|
+
snapshot?.update && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateNotice, { update: snapshot.update, host, phase }),
|
|
601
660
|
body(),
|
|
602
661
|
pending && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lark-pending", role: "status", children: [
|
|
603
662
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "lark-spinner", "aria-hidden": "true" }),
|
|
@@ -612,10 +671,10 @@ function ConnectionCard({ api }) {
|
|
|
612
671
|
// src/client/styles.ts
|
|
613
672
|
var css = `
|
|
614
673
|
.dsh-lark-settings{--lark-ink:#263247;--lark-muted:#738096;--lark-line:#e4eaf2;--lark-blue:#4268df;--lark-surface:#fff;--lark-soft:#f5f8fd;font:14px/1.65 -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;color:var(--lark-ink);max-width:720px;margin:0 auto;padding:8px 0 20px;box-sizing:border-box}
|
|
615
|
-
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
674
|
+
.dsh-lark-settings *{box-sizing:border-box}.dsh-lark-settings button,.dsh-lark-settings input{font:inherit}.dsh-lark-settings button,.dsh-lark-settings summary{cursor:pointer}.dsh-lark-settings button:disabled{opacity:.5;cursor:not-allowed}.dsh-lark-settings button:focus-visible,.dsh-lark-settings a:focus-visible,.dsh-lark-settings input:focus-visible,.dsh-lark-settings summary:focus-visible{outline:3px solid #a1b8ff;outline-offset:3px}.dsh-lark-settings a{overflow-wrap:anywhere}.lark-heading{margin:0 0 22px}.lark-heading h2{font-size:23px;font-weight:650;margin:0 0 5px;letter-spacing:-.4px}.lark-heading p,.lark-card-header p{color:var(--lark-muted);font-size:13px;margin:0}.lark-card{border:1px solid var(--lark-line);border-radius:15px;background:var(--lark-surface);overflow:hidden;box-shadow:0 2px 5px #27385504}.lark-card-header{display:flex;align-items:center;gap:12px;padding:19px 23px;border-bottom:1px solid var(--lark-line)}.lark-plugin-icon{width:43px;height:43px;object-fit:contain;flex-shrink:0}.lark-card-header h2{font-size:15px;font-weight:650;line-height:1.6;margin:0}.lark-card-header p{font-size:12px}.lark-status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--lark-muted);white-space:nowrap}.lark-status i{width:6px;height:6px;border-radius:50%;background:#9da9bc}.lark-status.online{color:#248470}.lark-status.online i{background:#248470}.lark-body{padding:28px;position:relative}.lark-body h3{font-size:20px;font-weight:650;letter-spacing:-.25px;margin:0 0 9px;line-height:1.5}.lark-copy{color:var(--lark-muted);font-size:13px;margin:0 0 20px}.lark-centered{text-align:center;line-height:1.9}.lark-body .lark-intro-title{text-align:center;font-size:21px}.lark-brands{display:flex;justify-content:center;align-items:center;gap:16px;margin:5px 0 25px}.lark-brand{width:53px;height:53px;display:grid;place-items:center;border-radius:14px;background:var(--lark-soft);border:1px solid var(--lark-line)}.lark-brand img{width:37px;height:37px;object-fit:contain}.lark-brand-line{width:78px;height:1px;background:#dbe3ee;position:relative}.lark-brand-line:after{content:"";position:absolute;left:calc(50% - 3px);top:-3px;width:7px;height:7px;border-radius:50%;background:#a9bbdc;border:2px solid var(--lark-surface)}.lark-brands-connected .lark-brand-line{background:#99d1c1}.lark-brands-connected .lark-brand-line:after{background:#248470}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:38px;padding:8px 16px;border-radius:8px;text-decoration:none;font-size:13px;font-weight:550;border:1px solid transparent;line-height:1.5;transition:background .14s}.lark-primary{background:var(--lark-blue);color:#fff}.lark-primary:hover{background:#3559ca}.lark-secondary{color:#485b79;background:var(--lark-surface);border-color:#dce3ed}.lark-secondary:hover{background:var(--lark-soft)}.lark-quiet{background:transparent;color:var(--lark-muted);font-weight:450}.lark-quiet:hover{background:var(--lark-soft)}.lark-danger{color:#ad4949;border-color:#e5bcbc;background:var(--lark-surface)}.lark-text-button{display:inline-flex;align-items:center;gap:7px;background:transparent;border:0;padding:5px 0;color:#5875a5;font-size:12px}.lark-entry{display:flex;flex-direction:column;align-items:center;gap:10px}.lark-entry .lark-primary{min-width:200px}.lark-actions{display:flex;gap:9px;flex-wrap:wrap;align-items:center;margin-top:23px}.lark-spread{justify-content:space-between}.lark-center{justify-content:center}.lark-footnote{color:var(--lark-muted);font-size:11px;text-align:center;margin:18px 0 0}.lark-left{text-align:left}.lark-bottom-note{text-align:center;color:var(--lark-muted);font-size:11px;margin:15px 0 0}.lark-steps{display:flex;list-style:none;gap:12px;margin:0 0 25px;padding:0;color:#8b98ad;font-size:11px}.lark-steps li{display:flex;gap:6px;align-items:center;white-space:nowrap}.lark-steps li+li:before{content:"";width:23px;height:1px;background:var(--lark-line);margin-right:5px}.lark-steps li span{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;background:var(--lark-soft);font-size:10px}.lark-steps .active{color:var(--lark-blue);font-weight:600}.lark-steps .active span{background:#edf2ff}.lark-steps .done{color:#248470}.lark-steps .done span{background:#edf8f4}.lark-qr-layout{display:flex;align-items:center;gap:25px;margin:23px 0}.lark-qr{position:relative;width:166px;height:166px;padding:10px;background:white;border:1px solid var(--lark-line);border-radius:12px;flex-shrink:0}.lark-qr img{width:100%;height:100%;object-fit:contain}.lark-qr.expired img{opacity:.12}.lark-qr>span{position:absolute;inset:0;display:grid;place-items:center;color:#64758e;font-weight:600}.lark-qr-layout strong{font-weight:600;font-size:13px}.lark-qr-layout .lark-copy{margin:7px 0 10px}.lark-time{color:var(--lark-muted);font-size:11px;font-variant-numeric:tabular-nums}.lark-link-details{border-top:1px solid var(--lark-line);padding:13px 0 0;font-size:12px;color:var(--lark-muted)}.lark-link-details summary{margin-bottom:12px}.lark-raw-url{font-size:10px;line-height:1.65;overflow-wrap:anywhere;margin-top:10px;color:var(--lark-muted);user-select:all}.lark-notice{padding:13px 15px;background:#eef3fe;border-radius:9px;color:#526c95;font-size:12px;line-height:1.8}.lark-notice.warning{background:#fff7e9;color:#8b671e}.lark-state-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#edf8f4;color:#248470;font-size:20px;margin:2px 0 18px}.lark-state-icon.warning{background:#fff7e9;color:#9b7530}.lark-checks{padding:0;list-style:none;margin:20px 0;font-size:13px;color:var(--lark-muted)}.lark-checks li{padding:10px 0;border-bottom:1px solid var(--lark-line)}.lark-checks li:before{content:"\xB7";margin-right:12px}.lark-pairing{background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:11px;padding:17px 18px;display:flex;align-items:center;justify-content:space-between;gap:13px}.lark-pair-code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:21px;letter-spacing:.02em;color:#3d5a91;margin:4px 0 2px;overflow-wrap:anywhere;user-select:all}.lark-pairing.expired .lark-pair-code{opacity:.5}.lark-caption{font-size:11px;color:var(--lark-muted)}.lark-copy-status{min-height:18px;font-size:11px;color:#248470;margin:5px 0}.lark-instructions{padding-left:20px;color:#536784;font-size:13px}.lark-instructions li{padding:3px 0}.lark-owner{padding:17px 18px;background:var(--lark-soft);border:1px solid var(--lark-line);border-radius:10px;display:grid;gap:6px}.lark-owner strong{font-size:15px;font-weight:600;overflow-wrap:anywhere}.lark-connected-row{border-top:1px solid var(--lark-line);border-bottom:1px solid var(--lark-line);display:flex;justify-content:space-between;align-items:center;gap:16px;padding:16px 0;margin:26px 0 0}.lark-connected-row strong{font-size:12px;font-weight:600}.lark-connected-row p{font-size:11px;color:var(--lark-muted);margin:3px 0 0}.lark-advanced{font-size:12px;color:var(--lark-muted);padding-top:14px}.lark-advanced p{margin:9px 0 0;line-height:1.9}.lark-disconnect{border:1px solid #eacbcb;padding:14px;border-radius:10px;margin-top:16px;font-size:12px}.lark-disconnect p{margin:0}.lark-disconnect .lark-actions{margin-top:12px}.lark-profile-list{border:0;margin:20px 0 0;padding:0;display:grid;gap:10px;min-width:0}.lark-profile{display:flex;align-items:center;gap:12px;border:1px solid var(--lark-line);border-radius:10px;padding:15px;background:var(--lark-soft);cursor:pointer;overflow-wrap:anywhere}.lark-profile input{accent-color:var(--lark-blue)}.lark-error{display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff1f1;border:1px solid #f0d5d5;color:#9b4e4e;border-radius:9px;padding:11px 13px;font-size:12px;margin-bottom:21px}.lark-error .lark-text-button{flex-shrink:0;color:#9b4e4e}.lark-loading{padding:27px 0;text-align:center}.lark-loading .lark-spinner{margin:0 auto 17px;display:block}.lark-loading p{font-size:13px;color:var(--lark-muted);margin:0}.lark-loading-row{display:flex;align-items:center;gap:12px;color:var(--lark-muted);font-size:13px;margin:4px 0 15px}.lark-loading-row h3{color:var(--lark-ink);margin:0}.lark-spinner{display:inline-block;width:18px;height:18px;border:2px solid #dfe6f4;border-top-color:var(--lark-blue);border-radius:50%;animation:lark-spin .85s linear infinite;flex-shrink:0}.lark-pending{display:flex;align-items:center;gap:9px;margin-top:19px;font-size:11px;color:var(--lark-muted)}.lark-pending .lark-spinner{width:13px;height:13px}.lark-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.lark-update{border:1px solid #cfdcf8;background:#f4f7ff;border-radius:11px;padding:14px 16px;margin:0 0 22px}.lark-update-row{display:flex;align-items:center;justify-content:space-between;gap:14px}.lark-update strong{font-size:13px;font-weight:600}.lark-update p{font-size:12px;color:var(--lark-muted);margin:3px 0 0}.lark-update .lark-instructions{margin:12px 0 0}.lark-update .lark-actions{margin-top:12px}.lark-update .lark-copy-status{margin:6px 0 0}.lark-command{display:block;margin-top:6px;padding:8px 10px;border-radius:7px;background:var(--lark-surface);border:1px solid var(--lark-line);font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;user-select:all}
|
|
616
675
|
@keyframes lark-spin{to{transform:rotate(360deg)}}
|
|
617
|
-
@media(prefers-color-scheme:dark){.dsh-lark-settings{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-line:#364357;--lark-surface:#202a39;--lark-soft:#283448;--lark-blue:#5077ec}.lark-secondary{color:#c6d5ed;border-color:#46536b}.lark-pair-code{color:#b9ceff}.lark-instructions{color:#b0c2de}.lark-notice{background:#273d5a;color:#c2d3ec}.lark-notice.warning,.lark-state-icon.warning{background:#44391f;color:#e4c886}.lark-error{background:#492e32;border-color:#684047;color:#edbec2}.lark-error .lark-text-button{color:#edbec2}.lark-steps .active span{background:#293e6b}.lark-steps .done span,.lark-state-icon{background:#23483f}.lark-status.online,.lark-steps .done{color:#78c5b0}}
|
|
618
|
-
@media(max-width:520px){.lark-heading{margin-bottom:17px}.lark-card-header{padding:16px;gap:10px}.lark-card-header p{display:none}.lark-plugin-icon{width:37px;height:37px}.lark-body{padding:21px 18px}.lark-steps{gap:7px}.lark-steps li+li:before{width:12px;margin-right:2px}.lark-qr-layout{gap:17px;align-items:flex-start}.lark-qr{width:138px;height:138px}.lark-qr-layout strong{font-size:12px}.lark-qr-layout .lark-copy{font-size:12px}.lark-pairing{align-items:flex-start;flex-direction:column}.lark-pair-code{font-size:19px}.lark-body h3{font-size:19px}.lark-connected-row{gap:10px}.lark-actions{gap:8px}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{padding:8px 12px}.lark-copy{font-size:12px}}
|
|
676
|
+
@media(prefers-color-scheme:dark){.dsh-lark-settings{--lark-ink:#e0e7f1;--lark-muted:#96a6bd;--lark-line:#364357;--lark-surface:#202a39;--lark-soft:#283448;--lark-blue:#5077ec}.lark-secondary{color:#c6d5ed;border-color:#46536b}.lark-pair-code{color:#b9ceff}.lark-instructions{color:#b0c2de}.lark-notice{background:#273d5a;color:#c2d3ec}.lark-notice.warning,.lark-state-icon.warning{background:#44391f;color:#e4c886}.lark-error{background:#492e32;border-color:#684047;color:#edbec2}.lark-error .lark-text-button{color:#edbec2}.lark-steps .active span{background:#293e6b}.lark-steps .done span,.lark-state-icon{background:#23483f}.lark-status.online,.lark-steps .done{color:#78c5b0}.lark-update{background:#24324a;border-color:#3a4d70}}
|
|
677
|
+
@media(max-width:520px){.lark-heading{margin-bottom:17px}.lark-card-header{padding:16px;gap:10px}.lark-card-header p{display:none}.lark-plugin-icon{width:37px;height:37px}.lark-body{padding:21px 18px}.lark-steps{gap:7px}.lark-steps li+li:before{width:12px;margin-right:2px}.lark-qr-layout{gap:17px;align-items:flex-start}.lark-qr{width:138px;height:138px}.lark-qr-layout strong{font-size:12px}.lark-qr-layout .lark-copy{font-size:12px}.lark-pairing{align-items:flex-start;flex-direction:column}.lark-pair-code{font-size:19px}.lark-body h3{font-size:19px}.lark-connected-row{gap:10px}.lark-actions{gap:8px}.lark-primary,.lark-secondary,.lark-quiet,.lark-danger{padding:8px 12px}.lark-copy{font-size:12px}.lark-update-row{flex-direction:column;align-items:flex-start}}
|
|
619
678
|
@media(max-width:360px){.lark-qr-layout{flex-direction:column;align-items:center;text-align:center}.lark-steps{font-size:10px}.lark-steps li+li:before{width:8px}.lark-status{font-size:10px}}
|
|
620
679
|
@media(prefers-reduced-motion:reduce){.dsh-lark-settings *{animation:none!important;transition:none!important}}
|
|
621
680
|
`;
|
|
@@ -630,14 +689,25 @@ function installStyles() {
|
|
|
630
689
|
// src/client/index.tsx
|
|
631
690
|
var inject = ["slots", "connection"];
|
|
632
691
|
function apply(ctx) {
|
|
633
|
-
const
|
|
692
|
+
const app = ctx.connection.generation !== void 0;
|
|
693
|
+
const api = new ConnectionApi(ctx.connection.rpc, app);
|
|
694
|
+
const openPlugins = () => {
|
|
695
|
+
try {
|
|
696
|
+
const navigation = ctx.get?.("pluginNavigation");
|
|
697
|
+
if (typeof navigation?.openList !== "function") return false;
|
|
698
|
+
navigation.openList();
|
|
699
|
+
return true;
|
|
700
|
+
} catch {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
634
704
|
ctx.effect(installStyles, "dsh-larkbot: connection styles");
|
|
635
705
|
ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
|
|
636
706
|
name: "settings.plugins.tab",
|
|
637
707
|
id: "dsh-larkbot",
|
|
638
708
|
order: 30,
|
|
639
709
|
label: () => "\u98DE\u4E66",
|
|
640
|
-
inject: () => ({ api })
|
|
710
|
+
inject: () => ({ api, host: { app, openPlugins } })
|
|
641
711
|
}, ConnectionCard));
|
|
642
712
|
}
|
|
643
713
|
return module.exports; } });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import { ConnectionController } from './controller.js';
|
|
3
3
|
import type { ConnectionResult } from './protocol.js';
|
|
4
|
+
import { type Release } from './updates.js';
|
|
4
5
|
interface Peer {
|
|
5
6
|
principal?: {
|
|
6
7
|
kind?: string;
|
|
@@ -27,9 +28,13 @@ export declare function lockedConnectionControl(): ConnectionControl;
|
|
|
27
28
|
export declare function registerConnectionRpc(rpc: RpcHost, controller: ConnectionControl): () => Promise<void>;
|
|
28
29
|
export declare function registerConnectionRpc(rpc: RpcHost, controller: ConnectionControl, ownerRoutes?: OwnerFetchRoutes): (() => Promise<void>) | Promise<() => Promise<void>>;
|
|
29
30
|
export declare function findLarkExecutable(explicit?: string): Promise<string>;
|
|
30
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Attach the control plane without requiring any user-written team configuration.
|
|
33
|
+
* `latestRelease` replaces the npm lookup behind the upgrade notice (tests use it to stay offline).
|
|
34
|
+
*/
|
|
31
35
|
export declare function applyOnboarding(ctx: Context, options?: {
|
|
32
36
|
dataDir?: string;
|
|
33
37
|
executable?: string;
|
|
38
|
+
latestRelease?: (signal: AbortSignal) => Promise<Release | undefined>;
|
|
34
39
|
}): Promise<void>;
|
|
35
40
|
export {};
|
package/dist/onboarding/host.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { access } from 'node:fs/promises';
|
|
3
|
-
import { constants } from 'node:fs';
|
|
3
|
+
import { constants, readFileSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { delimiter, join, resolve } from 'node:path';
|
|
6
6
|
import { createBotService, BotServiceStartError } from '../bot-service.js';
|
|
@@ -9,6 +9,7 @@ import { acquireLock, LockHeldError } from '../lock.js';
|
|
|
9
9
|
import { ConnectionController, SetupError } from './controller.js';
|
|
10
10
|
import { ConnectionStore } from './store.js';
|
|
11
11
|
import { LarkPlatform } from './lark-platform.js';
|
|
12
|
+
import { npmLatest, UpdateCheck } from './updates.js';
|
|
12
13
|
const endpoints = new Set(['status', 'profiles', 'create', 'use-profile', 'recheck', 'cancel',
|
|
13
14
|
'renew-pairing', 'confirm-owner', 'pause', 'resume', 'disconnect', 'enable-documents', 'cancel-documents']);
|
|
14
15
|
const failure = (message) => ({ ok: false, error: { code: 'internal', message, details: {} } });
|
|
@@ -151,6 +152,21 @@ export async function findLarkExecutable(explicit) {
|
|
|
151
152
|
}
|
|
152
153
|
return 'lark-cli';
|
|
153
154
|
}
|
|
155
|
+
/** This plugin's own package name and version, from the package it was installed from. */
|
|
156
|
+
function installedPackage() {
|
|
157
|
+
try {
|
|
158
|
+
const value = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
|
|
159
|
+
return typeof value.name === 'string' && typeof value.version === 'string' ? { name: value.name, version: value.version } : undefined;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Every snapshot the page receives carries the upgrade notice once a newer release is known. */
|
|
166
|
+
function withUpdate(snapshot, updates) {
|
|
167
|
+
const update = updates.status();
|
|
168
|
+
return update ? { ...snapshot, update } : snapshot;
|
|
169
|
+
}
|
|
154
170
|
function requireRuntime(ctx) {
|
|
155
171
|
if (!checkRuntimeCompatibility(ctx).supported)
|
|
156
172
|
throw new SetupError('当前 dsh 缺少所需的会话能力,请检查插件和持久化服务。', false);
|
|
@@ -185,7 +201,10 @@ async function waitReady(service, signal) {
|
|
|
185
201
|
abort();
|
|
186
202
|
});
|
|
187
203
|
}
|
|
188
|
-
/**
|
|
204
|
+
/**
|
|
205
|
+
* Attach the control plane without requiring any user-written team configuration.
|
|
206
|
+
* `latestRelease` replaces the npm lookup behind the upgrade notice (tests use it to stay offline).
|
|
207
|
+
*/
|
|
189
208
|
export async function applyOnboarding(ctx, options = {}) {
|
|
190
209
|
const connection = ctx.get('connection');
|
|
191
210
|
if (!connection?.rpc)
|
|
@@ -240,9 +259,17 @@ export async function applyOnboarding(ctx, options = {}) {
|
|
|
240
259
|
throw error;
|
|
241
260
|
}
|
|
242
261
|
}, });
|
|
262
|
+
const installed = installedPackage();
|
|
263
|
+
const updates = new UpdateCheck(installed?.version, options.latestRelease
|
|
264
|
+
?? (signal => installed ? npmLatest(installed.name, signal) : Promise.resolve(undefined)));
|
|
265
|
+
const control = {
|
|
266
|
+
snapshot: () => withUpdate(controller.snapshot(), updates),
|
|
267
|
+
profiles: signal => controller.profiles(signal),
|
|
268
|
+
command: async (endpoint, payload) => withUpdate(await controller.command(endpoint, payload), updates),
|
|
269
|
+
};
|
|
243
270
|
let remove;
|
|
244
271
|
try {
|
|
245
|
-
remove = await registerConnectionRpc(connection.rpc,
|
|
272
|
+
remove = await registerConnectionRpc(connection.rpc, control, connection.fetch);
|
|
246
273
|
ctx.effect(() => {
|
|
247
274
|
return async () => {
|
|
248
275
|
await remove();
|
|
@@ -40,6 +40,11 @@ export interface ConnectionSnapshot {
|
|
|
40
40
|
expiresAt: number;
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
+
/** A newer published release of this plugin, with the installed version. */
|
|
44
|
+
update?: {
|
|
45
|
+
current: string;
|
|
46
|
+
latest: string;
|
|
47
|
+
};
|
|
43
48
|
}
|
|
44
49
|
export interface ConnectionProfile {
|
|
45
50
|
name: string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** A published release and when npm says it was published (epoch milliseconds). */
|
|
2
|
+
export interface Release {
|
|
3
|
+
version: string;
|
|
4
|
+
publishedAt: number;
|
|
5
|
+
}
|
|
6
|
+
/** True when `next` is a later release than `current`; a prerelease comes before its release. */
|
|
7
|
+
export declare function newerVersion(next: string, current: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* npm's `latest` release of a package and its publish time, when it names a stable release.
|
|
10
|
+
* Only the full package document carries publish times. Sends only the package name.
|
|
11
|
+
*/
|
|
12
|
+
export declare function npmLatest(name: string, signal: AbortSignal): Promise<Release | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* Whether a newer release of this plugin is available, for the settings page.
|
|
15
|
+
* A status read never waits for the registry: it returns what is known and
|
|
16
|
+
* starts a background check when one is due. Failures just show no notice.
|
|
17
|
+
* A release younger than a day is not offered yet, since installing it would
|
|
18
|
+
* still pick an older one; it shows up once it ages, without another check.
|
|
19
|
+
*/
|
|
20
|
+
export declare class UpdateCheck {
|
|
21
|
+
private readonly current;
|
|
22
|
+
private readonly lookup;
|
|
23
|
+
private readonly now;
|
|
24
|
+
private latest?;
|
|
25
|
+
private nextCheck;
|
|
26
|
+
private running;
|
|
27
|
+
constructor(current: string | undefined, lookup: (signal: AbortSignal) => Promise<Release | undefined>, now?: () => number);
|
|
28
|
+
status(): {
|
|
29
|
+
current: string;
|
|
30
|
+
latest: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
private refresh;
|
|
33
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Read only while the settings page polls, and at most this often once a check succeeds.
|
|
2
|
+
const CHECK_EVERY_MS = 6 * 60 * 60_000;
|
|
3
|
+
// A failed check (offline, blocked registry) waits this long before the next try.
|
|
4
|
+
const RETRY_AFTER_MS = 30 * 60_000;
|
|
5
|
+
// pnpm 11, which the App and `dsh plugin` install with, skips releases younger than a day
|
|
6
|
+
// (`minimumReleaseAge`) and installs an older one instead, so a release is offered only after that.
|
|
7
|
+
const INSTALLABLE_AFTER_MS = 24 * 60 * 60_000;
|
|
8
|
+
const VERSION = /^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(?:-([0-9A-Za-z.-]{1,40}))?$/;
|
|
9
|
+
/** True when `next` is a later release than `current`; a prerelease comes before its release. */
|
|
10
|
+
export function newerVersion(next, current) {
|
|
11
|
+
const a = VERSION.exec(next), b = VERSION.exec(current);
|
|
12
|
+
if (!a || !b)
|
|
13
|
+
return false;
|
|
14
|
+
for (let index = 1; index <= 3; index++) {
|
|
15
|
+
const left = Number(a[index]), right = Number(b[index]);
|
|
16
|
+
if (left !== right)
|
|
17
|
+
return left > right;
|
|
18
|
+
}
|
|
19
|
+
return !a[4] && Boolean(b[4]);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* npm's `latest` release of a package and its publish time, when it names a stable release.
|
|
23
|
+
* Only the full package document carries publish times. Sends only the package name.
|
|
24
|
+
*/
|
|
25
|
+
export async function npmLatest(name, signal) {
|
|
26
|
+
const response = await fetch(`https://registry.npmjs.org/${name.replace('/', '%2f')}`, { signal, headers: { accept: 'application/json' } });
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
return;
|
|
29
|
+
const document = await response.json();
|
|
30
|
+
const latest = document?.['dist-tags']?.latest;
|
|
31
|
+
if (typeof latest !== 'string' || !VERSION.test(latest) || latest.includes('-'))
|
|
32
|
+
return;
|
|
33
|
+
const time = document?.time;
|
|
34
|
+
const stamp = time && typeof time === 'object' ? time[latest] : undefined;
|
|
35
|
+
const publishedAt = typeof stamp === 'string' ? Date.parse(stamp) : Number.NaN;
|
|
36
|
+
return Number.isFinite(publishedAt) ? { version: latest, publishedAt } : undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether a newer release of this plugin is available, for the settings page.
|
|
40
|
+
* A status read never waits for the registry: it returns what is known and
|
|
41
|
+
* starts a background check when one is due. Failures just show no notice.
|
|
42
|
+
* A release younger than a day is not offered yet, since installing it would
|
|
43
|
+
* still pick an older one; it shows up once it ages, without another check.
|
|
44
|
+
*/
|
|
45
|
+
export class UpdateCheck {
|
|
46
|
+
current;
|
|
47
|
+
lookup;
|
|
48
|
+
now;
|
|
49
|
+
latest;
|
|
50
|
+
nextCheck = 0;
|
|
51
|
+
running = false;
|
|
52
|
+
constructor(current, lookup, now = Date.now) {
|
|
53
|
+
this.current = current;
|
|
54
|
+
this.lookup = lookup;
|
|
55
|
+
this.now = now;
|
|
56
|
+
}
|
|
57
|
+
status() {
|
|
58
|
+
if (!this.current || !VERSION.test(this.current))
|
|
59
|
+
return;
|
|
60
|
+
if (!this.running && this.now() >= this.nextCheck)
|
|
61
|
+
void this.refresh();
|
|
62
|
+
const latest = this.latest;
|
|
63
|
+
if (!latest || this.now() - latest.publishedAt < INSTALLABLE_AFTER_MS)
|
|
64
|
+
return;
|
|
65
|
+
return newerVersion(latest.version, this.current) ? { current: this.current, latest: latest.version } : undefined;
|
|
66
|
+
}
|
|
67
|
+
async refresh() {
|
|
68
|
+
this.running = true;
|
|
69
|
+
this.nextCheck = this.now() + RETRY_AFTER_MS;
|
|
70
|
+
try {
|
|
71
|
+
const latest = await this.lookup(AbortSignal.timeout(10_000));
|
|
72
|
+
if (latest && VERSION.test(latest.version) && Number.isFinite(latest.publishedAt)) {
|
|
73
|
+
this.latest = { version: latest.version, publishedAt: latest.publishedAt };
|
|
74
|
+
this.nextCheck = this.now() + CHECK_EVERY_MS;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { /* Offline or blocked: no notice, and a later poll tries again. */ }
|
|
78
|
+
finally {
|
|
79
|
+
this.running = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=updates.js.map
|