dsh-theone 0.3.18 → 0.3.20
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 +7 -15
- package/README.zh.md +7 -15
- package/dist/client-locales.d.ts +2 -0
- package/dist/client-locales.js +4 -0
- package/dist/client.js +59 -23
- package/dist/update.d.ts +11 -1
- package/dist/update.js +47 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,18 +18,10 @@ How many sessions are sitting in your DSH sidebar? Getting back to last week's w
|
|
|
18
18
|
|
|
19
19
|
**TheOne takes care of that.** You just talk in one main chat:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
→ Video Attention Thesis
|
|
26
|
-
You: and add the Qwen speed numbers from earlier
|
|
27
|
-
→ Video Attention Thesis · reference: Qwen / RX 9070 XT
|
|
28
|
-
You: wrong topic, that was the GPU one
|
|
29
|
-
→ Qwen / RX 9070 XT (the previous message is redone there, and remembered)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
<sub>Illustration. By default, a one-line notice appears only when the topic changes.</sub>
|
|
21
|
+
<picture>
|
|
22
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/YunongDai2005/dsh-theone/main/docs/images/theone-architecture-en-dark.svg">
|
|
23
|
+
<img alt="How TheOne works: main chat sends each message through the router to its topic session; answers stream back, and the topic directory keeps progress, constraints and links" src="https://raw.githubusercontent.com/YunongDai2005/dsh-theone/main/docs/images/theone-architecture-en-light.svg" width="100%">
|
|
24
|
+
</picture>
|
|
33
25
|
|
|
34
26
|
Each project gets its own background session that reasons, runs tools and compacts on its own. What you see is always one ordinary conversation.
|
|
35
27
|
|
|
@@ -47,7 +39,7 @@ Each project gets its own background session that reasons, runs tools and compac
|
|
|
47
39
|
- **Related work connects; unrelated work stays out.** Related topics share progress automatically and TheOne learns which ones belong together from how you use them. A topic's constraints (say, "budget figures stay out of the paper") are attached verbatim every time, so compaction never drops them.
|
|
48
40
|
- **Gets better with use.** Say "wrong topic" or click **Move to…** in the directory, and TheOne learns which terms tie that kind of message to the right topic (one small model call), so similar messages go there from then on. The directory shows how often routing was kept as is.
|
|
49
41
|
- **Your old sessions become a topic directory.** After install it reads your existing sessions in the background, turns them into topics grouped into workspaces, and you pick up where you left off.
|
|
50
|
-
- **Nothing extra to configure.** No separate API key: routing and the background sessions use the model you chose in DSH. When TheOne is the selected model, a
|
|
42
|
+
- **Nothing extra to configure.** No separate API key: routing and the background sessions use the model you chose in DSH. When TheOne is the selected model, a button with a layers icon appears beside the model menu: it shows which model does the routing and the work, and switches it.
|
|
51
43
|
|
|
52
44
|

|
|
53
45
|
|
|
@@ -57,7 +49,7 @@ Each project gets its own background session that reasons, runs tools and compac
|
|
|
57
49
|
2. Open **Plugins → Add plugin**, paste `https://github.com/YunongDai2005/dsh-theone`, and install.
|
|
58
50
|
3. Click **TheOne · Main chat** in the sidebar and start talking.
|
|
59
51
|
|
|
60
|
-
From the command line: `dsh plugin --profile web add github:YunongDai2005/dsh-theone --ignore-scripts`. Works with DSH `0.2.0-rc.2` and Node.js 24; the interface follows DSH's language (English / Simplified Chinese). Installing `dsh-theone` from npm also works, but DSH's package manager only accepts a version once it has been published for 24 hours, so the GitHub address gets new versions first. When an update is newer than that, the
|
|
52
|
+
From the command line: `dsh plugin --profile web add github:YunongDai2005/dsh-theone --ignore-scripts`. Works with DSH `0.2.0-rc.2` and Node.js 24; the interface follows DSH's language (English / Simplified Chinese). Installing `dsh-theone` from npm also works, but DSH's package manager only accepts a version once it has been published for 24 hours, so the GitHub address gets new versions first. When an update is newer than that, the update button explains it and can exempt TheOne alone so it installs right away.
|
|
61
53
|
|
|
62
54
|
> Unofficial community project, maintained independently. It is not affiliated with or endorsed by DeepSeek.
|
|
63
55
|
|
|
@@ -106,7 +98,7 @@ The database location and entry identifier switch TheOne to different data, so t
|
|
|
106
98
|
- A topic marked **Do not share** never appears in other topics' briefings, recent-chat excerpts or lookups.
|
|
107
99
|
- Notices from the author are read from `https://yulid.org/theone/notice.json` with a plain request that sends none of your data; turn them off in Settings.
|
|
108
100
|
- When main chat grows long, DSH compacts it through TheOne: frequently used topics keep longer summaries and their latest turns, rarely used ones keep a short status. This makes no model call.
|
|
109
|
-
- When a new version is out,
|
|
101
|
+
- When a new version is out, a download icon appears on the right of the TheOne entry in the sidebar: one click installs it through DSH's plugin manager and reloads TheOne in place, without restarting DSH (a DSH without plugin hot reload applies it at the next restart). Your topics stay in the database.
|
|
110
102
|
</details>
|
|
111
103
|
|
|
112
104
|
<details>
|
package/README.zh.md
CHANGED
|
@@ -18,18 +18,10 @@
|
|
|
18
18
|
|
|
19
19
|
**TheOne 把这些都交给它。** 你只管在一个主聊天里说话:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
→ 视频注意力论文
|
|
26
|
-
你:把刚才 Qwen 的测速结果也放进去
|
|
27
|
-
→ 视频注意力论文 · 参考:Qwen / RX 9070 XT
|
|
28
|
-
你:分错了,是显卡那个的
|
|
29
|
-
→ Qwen / RX 9070 XT (上一条交给它重新处理,以后也记住了)
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
<sub>示意。默认只在切换话题时显示一行提示。</sub>
|
|
21
|
+
<picture>
|
|
22
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/YunongDai2005/dsh-theone/main/docs/images/theone-architecture-zh-dark.svg">
|
|
23
|
+
<img alt="TheOne 的结构:主聊天把每条消息交给分配话题,送到对应的后台会话,结果实时回到主聊天;话题目录保存进展、约束和关联" src="https://raw.githubusercontent.com/YunongDai2005/dsh-theone/main/docs/images/theone-architecture-zh-light.svg" width="100%">
|
|
24
|
+
</picture>
|
|
33
25
|
|
|
34
26
|
每件事都有自己的后台会话,在里面推理、调用工具、压缩上下文;你看到的始终是一个普通的对话。
|
|
35
27
|
|
|
@@ -47,7 +39,7 @@
|
|
|
47
39
|
- **相关的事会互通,无关的事互不打扰**:相关话题自动共享进展,并从你的使用中学习哪些话题有关联;话题的约束(比如「预算数字不能写进论文」)每次都原样带上,压缩不会把它丢掉。
|
|
48
40
|
- **越用越准**:说「分错了」或在目录里点「改到…」,TheOne 会学到哪些词把这类消息和正确话题联系起来(只需一次很小的模型调用),类似的消息以后直接分对;话题目录里能看到分配的准确率。
|
|
49
41
|
- **旧会话自动变成话题目录**:装上后它会在后台读取你已有的会话,整理成话题并按工作区分组,直接接着聊。
|
|
50
|
-
- **零额外配置**:不用另填 API Key,路由和后台都用你在 DSH 里选好的模型。选中 TheOne
|
|
42
|
+
- **零额外配置**:不用另填 API Key,路由和后台都用你在 DSH 里选好的模型。选中 TheOne 时,模型按钮旁边会出现一个带图层图标的按钮,显示并切换分配话题和干活用的模型。
|
|
51
43
|
|
|
52
44
|

|
|
53
45
|
|
|
@@ -57,7 +49,7 @@
|
|
|
57
49
|
2. 打开 **插件 → 添加插件**,粘贴 `https://github.com/YunongDai2005/dsh-theone`,点击安装。
|
|
58
50
|
3. 点击左侧 **TheOne · 主聊天**,开始说话。
|
|
59
51
|
|
|
60
|
-
命令行安装:`dsh plugin --profile web add github:YunongDai2005/dsh-theone --ignore-scripts`。兼容 DSH `0.2.0-rc.2` 与 Node.js 24;界面跟随 DSH 的语言(简体中文 / English)。也可以从 npm 安装 `dsh-theone`,但 DSH 的包管理器只接受发布满 24 小时的版本,所以用 GitHub
|
|
52
|
+
命令行安装:`dsh plugin --profile web add github:YunongDai2005/dsh-theone --ignore-scripts`。兼容 DSH `0.2.0-rc.2` 与 Node.js 24;界面跟随 DSH 的语言(简体中文 / English)。也可以从 npm 安装 `dsh-theone`,但 DSH 的包管理器只接受发布满 24 小时的版本,所以用 GitHub 地址安装能最早拿到新版本。遇到这种情况,更新按钮会说明原因,并可以只为 TheOne 放行、立即安装。
|
|
61
53
|
|
|
62
54
|
> 非官方社区项目,由社区成员独立维护,与 DeepSeek 不存在隶属或背书关系。
|
|
63
55
|
|
|
@@ -106,7 +98,7 @@
|
|
|
106
98
|
- 标记为「不共享」的话题,不会出现在其他话题的简报、最近对话和查阅结果里。
|
|
107
99
|
- 作者发布的公告从 `https://yulid.org/theone/notice.json` 读取,只是一次普通的下载,不发送你的任何数据;可以在设置里关闭。
|
|
108
100
|
- 主聊天变长时,DSH 通过 TheOne 压缩它:常用话题保留较长摘要和最近几轮,不常用的只保留简短状态,不额外调用模型。
|
|
109
|
-
- 有新版本时,左侧 TheOne
|
|
101
|
+
- 有新版本时,左侧 TheOne 入口右边会出现一个下载图标,点一下由 DSH 的插件管理器安装,并就地重新加载 TheOne,不用重启 DSH(不支持插件热加载的 DSH 会在下次启动时生效);话题都保存在数据库里,不受影响。
|
|
110
102
|
</details>
|
|
111
103
|
|
|
112
104
|
<details>
|
package/dist/client-locales.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export declare const zh: {
|
|
|
39
39
|
readonly 'update.busy': "有回复正在进行,等它结束后再点更新。";
|
|
40
40
|
readonly 'update.tooNew': "DSH 的安全策略只允许安装发布满 24 小时的版本,这一版还太新。过一阵再点一次即可。";
|
|
41
41
|
readonly 'update.network': "连不上下载源,请检查网络后点击重试。";
|
|
42
|
+
readonly 'update.otherAge': "pnpm 拒绝安装:{names} 也是发布不满 24 小时的版本,不归 TheOne 放行。等它满 24 小时后再点一次。";
|
|
43
|
+
readonly 'update.ageStill': "已为 TheOne 放行,pnpm 仍然拒绝:{detail}。完整日志在 DSH 配置目录的 .plugin-manager/logs 里。";
|
|
42
44
|
readonly 'update.failed': "更新失败";
|
|
43
45
|
readonly 'update.hint': "当前 {current},可更新到 {latest}。点击一键更新,不用重启 DSH。";
|
|
44
46
|
readonly 'update.manualHint': "当前 {current},最新 {latest}。这份插件不是从 GitHub 或 npm 安装的,请在「插件」页面重新安装。";
|
package/dist/client-locales.js
CHANGED
|
@@ -39,6 +39,8 @@ export const zh = {
|
|
|
39
39
|
'update.busy': '有回复正在进行,等它结束后再点更新。',
|
|
40
40
|
'update.tooNew': 'DSH 的安全策略只允许安装发布满 24 小时的版本,这一版还太新。过一阵再点一次即可。',
|
|
41
41
|
'update.network': '连不上下载源,请检查网络后点击重试。',
|
|
42
|
+
'update.otherAge': 'pnpm 拒绝安装:{names} 也是发布不满 24 小时的版本,不归 TheOne 放行。等它满 24 小时后再点一次。',
|
|
43
|
+
'update.ageStill': '已为 TheOne 放行,pnpm 仍然拒绝:{detail}。完整日志在 DSH 配置目录的 .plugin-manager/logs 里。',
|
|
42
44
|
'update.failed': '更新失败',
|
|
43
45
|
'update.hint': '当前 {current},可更新到 {latest}。点击一键更新,不用重启 DSH。',
|
|
44
46
|
'update.manualHint': '当前 {current},最新 {latest}。这份插件不是从 GitHub 或 npm 安装的,请在「插件」页面重新安装。',
|
|
@@ -248,6 +250,8 @@ export const en = {
|
|
|
248
250
|
'update.busy': 'A reply is in progress. Update once it finishes.',
|
|
249
251
|
'update.tooNew': 'DSH only installs versions published at least 24 hours ago, as a safety policy, and this one is newer. Try again later.',
|
|
250
252
|
'update.network': 'Could not reach the download source. Check the network and click to retry.',
|
|
253
|
+
'update.otherAge': 'pnpm refused: {names} is also less than 24 hours old, and only TheOne is exempted. Try again once it is a day old.',
|
|
254
|
+
'update.ageStill': 'TheOne is exempted, but pnpm still refused: {detail}. The full log is in .plugin-manager/logs in the DSH profile folder.',
|
|
251
255
|
'update.failed': 'Update failed',
|
|
252
256
|
'update.hint': 'You have {current}; {latest} is available. Click to update without restarting DSH.',
|
|
253
257
|
'update.manualHint': 'You have {current}; {latest} is available. This copy was not installed from GitHub or npm, so reinstall it from the Plugins page.',
|
package/dist/client.js
CHANGED
|
@@ -123,6 +123,8 @@ var zh = {
|
|
|
123
123
|
"update.busy": "\u6709\u56DE\u590D\u6B63\u5728\u8FDB\u884C\uFF0C\u7B49\u5B83\u7ED3\u675F\u540E\u518D\u70B9\u66F4\u65B0\u3002",
|
|
124
124
|
"update.tooNew": "DSH \u7684\u5B89\u5168\u7B56\u7565\u53EA\u5141\u8BB8\u5B89\u88C5\u53D1\u5E03\u6EE1 24 \u5C0F\u65F6\u7684\u7248\u672C\uFF0C\u8FD9\u4E00\u7248\u8FD8\u592A\u65B0\u3002\u8FC7\u4E00\u9635\u518D\u70B9\u4E00\u6B21\u5373\u53EF\u3002",
|
|
125
125
|
"update.network": "\u8FDE\u4E0D\u4E0A\u4E0B\u8F7D\u6E90\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u70B9\u51FB\u91CD\u8BD5\u3002",
|
|
126
|
+
"update.otherAge": "pnpm \u62D2\u7EDD\u5B89\u88C5\uFF1A{names} \u4E5F\u662F\u53D1\u5E03\u4E0D\u6EE1 24 \u5C0F\u65F6\u7684\u7248\u672C\uFF0C\u4E0D\u5F52 TheOne \u653E\u884C\u3002\u7B49\u5B83\u6EE1 24 \u5C0F\u65F6\u540E\u518D\u70B9\u4E00\u6B21\u3002",
|
|
127
|
+
"update.ageStill": "\u5DF2\u4E3A TheOne \u653E\u884C\uFF0Cpnpm \u4ECD\u7136\u62D2\u7EDD\uFF1A{detail}\u3002\u5B8C\u6574\u65E5\u5FD7\u5728 DSH \u914D\u7F6E\u76EE\u5F55\u7684 .plugin-manager/logs \u91CC\u3002",
|
|
126
128
|
"update.failed": "\u66F4\u65B0\u5931\u8D25",
|
|
127
129
|
"update.hint": "\u5F53\u524D {current}\uFF0C\u53EF\u66F4\u65B0\u5230 {latest}\u3002\u70B9\u51FB\u4E00\u952E\u66F4\u65B0\uFF0C\u4E0D\u7528\u91CD\u542F DSH\u3002",
|
|
128
130
|
"update.manualHint": "\u5F53\u524D {current}\uFF0C\u6700\u65B0 {latest}\u3002\u8FD9\u4EFD\u63D2\u4EF6\u4E0D\u662F\u4ECE GitHub \u6216 npm \u5B89\u88C5\u7684\uFF0C\u8BF7\u5728\u300C\u63D2\u4EF6\u300D\u9875\u9762\u91CD\u65B0\u5B89\u88C5\u3002",
|
|
@@ -332,6 +334,8 @@ var en = {
|
|
|
332
334
|
"update.busy": "A reply is in progress. Update once it finishes.",
|
|
333
335
|
"update.tooNew": "DSH only installs versions published at least 24 hours ago, as a safety policy, and this one is newer. Try again later.",
|
|
334
336
|
"update.network": "Could not reach the download source. Check the network and click to retry.",
|
|
337
|
+
"update.otherAge": "pnpm refused: {names} is also less than 24 hours old, and only TheOne is exempted. Try again once it is a day old.",
|
|
338
|
+
"update.ageStill": "TheOne is exempted, but pnpm still refused: {detail}. The full log is in .plugin-manager/logs in the DSH profile folder.",
|
|
335
339
|
"update.failed": "Update failed",
|
|
336
340
|
"update.hint": "You have {current}; {latest} is available. Click to update without restarting DSH.",
|
|
337
341
|
"update.manualHint": "You have {current}; {latest} is available. This copy was not installed from GitHub or npm, so reinstall it from the Plugins page.",
|
|
@@ -598,7 +602,7 @@ function apply(ctx) {
|
|
|
598
602
|
cache: "no-store",
|
|
599
603
|
...body ? { headers: { "content-type": "application/json" }, body: JSON.stringify(body) } : {}
|
|
600
604
|
});
|
|
601
|
-
if (response.ok || response.status === 409) setUpdate(await response.json());
|
|
605
|
+
if (response.ok || response.status === 409 || response.status === 400) setUpdate(await response.json());
|
|
602
606
|
if (update?.state === "reloading") void awaitReload(update.current);
|
|
603
607
|
} catch {
|
|
604
608
|
}
|
|
@@ -681,18 +685,47 @@ function apply(ctx) {
|
|
|
681
685
|
document.body.append(backdrop);
|
|
682
686
|
(allow ?? footer.querySelector("button"))?.focus();
|
|
683
687
|
}
|
|
688
|
+
const ICONS = {
|
|
689
|
+
download: ["M12 4v11", "M7 10l5 5 5-5", "M5 20h14"],
|
|
690
|
+
restart: ["M20 12a8 8 0 1 1-2.34-5.66L20 8.5", "M20 4v4.5h-4.5"],
|
|
691
|
+
alert: ["M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0", "M12 7.5v5.5", "M12 16.5h.01"],
|
|
692
|
+
notice: ["M4 10v4h3l6 4V6L7 10H4z", "M16.5 9a4 4 0 0 1 0 6", "M19 6.5a7.5 7.5 0 0 1 0 11"],
|
|
693
|
+
external: ["M14 4h6v6", "M20 4l-9 9", "M18 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h5"],
|
|
694
|
+
close: ["M6 6l12 12", "M18 6L6 18"],
|
|
695
|
+
layers: ["M12 3.5l8.5 4.5-8.5 4.5L3.5 8z", "M3.5 12.5l8.5 4.5 8.5-4.5", "M3.5 16.5l8.5 4.5 8.5-4.5"],
|
|
696
|
+
chevron: ["M6 15l6-6 6 6"],
|
|
697
|
+
grid: ["M4 4h6.5v6.5H4z", "M13.5 4H20v6.5h-6.5z", "M4 13.5h6.5V20H4z", "M13.5 13.5H20V20h-6.5z"]
|
|
698
|
+
};
|
|
699
|
+
const icon = (name, size = 16) => (0, import_react.createElement)(
|
|
700
|
+
"svg",
|
|
701
|
+
{
|
|
702
|
+
className: "theone-icon",
|
|
703
|
+
viewBox: "0 0 24 24",
|
|
704
|
+
width: size,
|
|
705
|
+
height: size,
|
|
706
|
+
fill: "none",
|
|
707
|
+
stroke: "currentColor",
|
|
708
|
+
strokeWidth: 2,
|
|
709
|
+
strokeLinecap: "round",
|
|
710
|
+
strokeLinejoin: "round",
|
|
711
|
+
"aria-hidden": true,
|
|
712
|
+
focusable: false
|
|
713
|
+
},
|
|
714
|
+
...ICONS[name].map((d) => (0, import_react.createElement)("path", { key: d, d }))
|
|
715
|
+
);
|
|
684
716
|
function UpdateButton() {
|
|
685
717
|
const t2 = useText();
|
|
686
718
|
const status = (0, import_react.useSyncExternalStore)(subscribeUpdate, () => update);
|
|
687
719
|
const waiting = !!status?.waiting && !status.available && !status.state;
|
|
688
720
|
if (!status || !status.available && !status.state && !waiting) return null;
|
|
689
721
|
const label = waiting ? t2("update.waiting") : status.state === "installing" ? t2("update.installing") : status.state === "reloading" ? t2("update.reloading") : status.state === "restart" ? t2("update.restart") : status.state === "failed" ? t2("update.failed") : t2("update.available");
|
|
690
|
-
const
|
|
722
|
+
const hint = waiting ? t2("update.waitingHint", { latest: status.waiting.version }) : status.error === "GATEWAY_BUSY" ? t2("update.busy") : status.state === "reloading" ? t2("update.reloadingHint") : status.state === "restart" ? t2("update.restartHint") : status.state === "failed" ? status.error === "OTHER_RELEASE_AGE" ? t2("update.otherAge", { names: status.detail ?? "" }) : status.error === "MINIMUM_RELEASE_AGE" ? status.exempt ? t2("update.ageStill", { detail: status.detail ?? "" }) : t2("update.tooNew") : status.error === "NETWORK" ? t2("update.network") : t2("update.failedHint", { error: status.detail ?? status.error ?? "" }) : status.installable ? t2("update.hint", { current: status.current, latest: status.latest ?? "" }) : t2("update.manualHint", { current: status.current, latest: status.latest ?? "" });
|
|
723
|
+
const title = `${label} \xB7 ${hint}`;
|
|
691
724
|
const act = (event) => {
|
|
692
725
|
event.preventDefault();
|
|
693
726
|
event.stopPropagation();
|
|
694
727
|
if (status.state === "installing" || status.state === "reloading" || status.state === "restart") return;
|
|
695
|
-
if (waiting || status.state === "failed" && status.error === "MINIMUM_RELEASE_AGE") {
|
|
728
|
+
if (!status.exempt && (waiting || status.state === "failed" && status.error === "MINIMUM_RELEASE_AGE")) {
|
|
696
729
|
openReleaseAgeDialog(status);
|
|
697
730
|
return;
|
|
698
731
|
}
|
|
@@ -718,9 +751,7 @@ function apply(ctx) {
|
|
|
718
751
|
if (event.key === "Enter" || event.key === " ") act(event);
|
|
719
752
|
}
|
|
720
753
|
},
|
|
721
|
-
status.state === "installing" || status.state === "reloading" ? (0, import_react.createElement)("span", { className: "theone-update-spin", "aria-hidden": true }) :
|
|
722
|
-
label,
|
|
723
|
-
!status.state && (waiting ? status.waiting.version : status.latest) ? (0, import_react.createElement)("small", null, `v${waiting ? status.waiting.version : status.latest}`) : null
|
|
754
|
+
status.state === "installing" || status.state === "reloading" ? (0, import_react.createElement)("span", { className: "theone-update-spin", "aria-hidden": true }) : icon(status.state === "restart" ? "restart" : status.state === "failed" ? "alert" : "download", 15)
|
|
724
755
|
);
|
|
725
756
|
}
|
|
726
757
|
const symbol = () => (0, import_react.createElement)(
|
|
@@ -1049,9 +1080,9 @@ function apply(ctx) {
|
|
|
1049
1080
|
}, () => setFailed(true));
|
|
1050
1081
|
}
|
|
1051
1082
|
},
|
|
1052
|
-
(0, import_react.createElement)("span", { className: "theone-bg-caption" },
|
|
1083
|
+
(0, import_react.createElement)("span", { className: "theone-bg-caption", "aria-hidden": true }, icon("layers", 15)),
|
|
1053
1084
|
(0, import_react.createElement)("span", { className: "theone-bg-text" }, label),
|
|
1054
|
-
(0, import_react.createElement)("span", { className: "theone-bg-chevron", "aria-hidden": true }, saving ? "\u2026" : "
|
|
1085
|
+
(0, import_react.createElement)("span", { className: "theone-bg-chevron", "aria-hidden": true }, saving ? "\u2026" : icon("chevron", 12))
|
|
1055
1086
|
);
|
|
1056
1087
|
}
|
|
1057
1088
|
let notices = [];
|
|
@@ -1157,13 +1188,19 @@ function apply(ctx) {
|
|
|
1157
1188
|
return (0, import_react.createElement)(
|
|
1158
1189
|
"div",
|
|
1159
1190
|
{ className: "theone-notice", role: "status", translate: "no" },
|
|
1160
|
-
(0, import_react.createElement)("span", { className: "theone-notice-tag"
|
|
1191
|
+
(0, import_react.createElement)("span", { className: "theone-notice-tag", title: t2("notice.label"), "aria-label": t2("notice.label"), role: "img" }, icon("notice", 14)),
|
|
1161
1192
|
(0, import_react.createElement)("span", { className: "theone-notice-text", title: `${title}
|
|
1162
1193
|
${body}` }, (0, import_react.createElement)("strong", null, title), " ", body),
|
|
1163
|
-
notice.link ? (0, import_react.createElement)("button", {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1194
|
+
notice.link ? (0, import_react.createElement)("button", {
|
|
1195
|
+
type: "button",
|
|
1196
|
+
className: "theone-notice-link",
|
|
1197
|
+
"aria-label": t2("notice.more"),
|
|
1198
|
+
title: t2("notice.more"),
|
|
1199
|
+
onClick: () => {
|
|
1200
|
+
window.open(notice.link, "_blank", "noopener");
|
|
1201
|
+
}
|
|
1202
|
+
}, icon("external", 14)) : null,
|
|
1203
|
+
(0, import_react.createElement)("button", { type: "button", className: "theone-notice-close", "aria-label": t2("notice.close"), title: t2("notice.close"), onClick: () => dismissNotice(notice.id) }, icon("close", 14))
|
|
1167
1204
|
);
|
|
1168
1205
|
}
|
|
1169
1206
|
ctx.inject(["slots", "modelDirectories"], (scope) => {
|
|
@@ -1912,7 +1949,7 @@ ${body}` }, (0, import_react.createElement)("strong", null, title), " ", body),
|
|
|
1912
1949
|
ctx.slots.register({ name: "sidebar.panellist", id: panelId, order: -1e3, label: () => t("gateway.title") }, SidebarEntry),
|
|
1913
1950
|
ctx.slots.register({ name: "sidebar.panellist", id: catalogPanelId, order: -999, label: () => t("catalog.title") }, ({ size }) => {
|
|
1914
1951
|
const t2 = useText();
|
|
1915
|
-
return (0, import_react.createElement)("span", { className: "theone-catalog-entry", translate: "no" }, (
|
|
1952
|
+
return (0, import_react.createElement)("span", { className: "theone-catalog-entry", translate: "no" }, icon("grid", 16), size === 16 ? (0, import_react.createElement)("span", null, t2("catalog.title")) : null);
|
|
1916
1953
|
})
|
|
1917
1954
|
]);
|
|
1918
1955
|
}
|
|
@@ -1933,10 +1970,10 @@ button:has(.theone-nav[data-active=true]){border-color:color-mix(in srgb,var(--o
|
|
|
1933
1970
|
.theone-update-dot{position:absolute;top:-2px;right:-2px;width:7px;height:7px;border-radius:50%;background:#e8590c;box-shadow:0 0 0 2px var(--one-tint)}
|
|
1934
1971
|
button:has(.theone-nav[data-wide=true])>span:has(.theone-nav){flex:1;min-width:0}
|
|
1935
1972
|
.theone-nav[data-wide=true]{width:100%}
|
|
1936
|
-
.theone-
|
|
1973
|
+
.theone-icon{display:block;flex:none;overflow:visible}
|
|
1974
|
+
.theone-update{margin-left:auto;align-self:center;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;box-sizing:border-box;padding:0;border-radius:50%;border:1px solid color-mix(in srgb,var(--one-accent) 45%,transparent);background:color-mix(in srgb,var(--one-accent) 12%,transparent);color:var(--one-accent);font-size:12px;line-height:16px;white-space:nowrap;cursor:pointer;transition:background 150ms ease}
|
|
1937
1975
|
.theone-update:hover{background:color-mix(in srgb,var(--one-accent) 22%,transparent)}
|
|
1938
1976
|
.theone-update:focus-visible{outline:2px solid var(--one-accent);outline-offset:2px}
|
|
1939
|
-
.theone-update small{font-size:11px;opacity:.75}
|
|
1940
1977
|
.theone-update[data-state=installing],.theone-update[data-state=reloading],.theone-update[data-state=restart]{cursor:default}
|
|
1941
1978
|
.theone-update[data-state=failed]{color:#d9480f;border-color:#d9480f66;background:#d9480f14}
|
|
1942
1979
|
.theone-update[data-state=waiting]{opacity:.8;border-style:dashed}
|
|
@@ -1953,23 +1990,22 @@ button:has(.theone-nav[data-wide=true])>span:has(.theone-nav){flex:1;min-width:0
|
|
|
1953
1990
|
.theone-dialog-actions button:focus-visible{outline:2px solid #4a7fc0;outline-offset:2px}
|
|
1954
1991
|
.theone-dialog .theone-dialog-body{white-space:pre-wrap;color:var(--dsw-alias-label-primary,#e8e8ea)}
|
|
1955
1992
|
.theone-notice{display:flex;align-items:center;gap:8px;margin:6px 4px 0;padding:6px 8px 6px 10px;border:1px solid var(--dsw-alias-border-l2,#ffffff1f);border-radius:10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#a0a0a6);min-width:0}
|
|
1956
|
-
.theone-notice-tag{flex:none;padding:
|
|
1993
|
+
.theone-notice-tag{flex:none;display:inline-flex;padding:3px;border-radius:6px;background:#3b6fb033;color:#7fa9dd}
|
|
1957
1994
|
.theone-notice-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1958
1995
|
.theone-notice-text strong{color:var(--dsw-alias-label-primary,#e8e8ea);font-weight:500}
|
|
1959
|
-
.theone-notice button{flex:none;border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;padding:
|
|
1996
|
+
.theone-notice button{flex:none;display:inline-flex;border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;padding:4px;border-radius:6px}
|
|
1960
1997
|
.theone-notice button:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff12)}
|
|
1961
1998
|
.theone-notice-link{color:#7fa9dd!important}
|
|
1962
|
-
.theone-
|
|
1963
|
-
.theone-update-spin{width:10px;height:10px;border-radius:50%;border:1.5px solid currentColor;border-right-color:transparent;animation:theone-spin 800ms linear infinite}
|
|
1999
|
+
.theone-update-spin{width:12px;height:12px;border-radius:50%;border:1.5px solid currentColor;border-right-color:transparent;animation:theone-spin 800ms linear infinite}
|
|
1964
2000
|
@keyframes theone-spin{to{transform:rotate(360deg)}}
|
|
1965
2001
|
@media(prefers-reduced-motion:reduce){.theone-update-spin{animation:none}}
|
|
1966
|
-
.theone-entry-copy{display:flex;flex-direction:column;align-items:flex-start;gap:2px}
|
|
2002
|
+
.theone-entry-copy{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}
|
|
1967
2003
|
.theone-entry-title{display:flex;align-items:center;gap:9px;line-height:22px}
|
|
1968
2004
|
.theone-wordmark{display:inline-flex;align-items:baseline;gap:1px;white-space:nowrap}
|
|
1969
2005
|
.theone-word-the{font-size:12px;font-weight:400;letter-spacing:-.25px;color:var(--dsw-alias-label-secondary)}
|
|
1970
2006
|
.theone-word-one{position:relative;font-family:ui-rounded,'SF Pro Rounded','Avenir Next',sans-serif;font-size:19px;line-height:1.15;font-weight:500;letter-spacing:-1px;transform:rotate(-4deg);padding-right:7px}
|
|
1971
2007
|
.theone-word-dot{position:absolute;right:0;top:2px;width:4px;height:4px;border-radius:50%;background:currentColor}
|
|
1972
|
-
.theone-entry-label{font-size:12px;font-weight:400}
|
|
2008
|
+
.theone-entry-label{font-size:12px;font-weight:400;white-space:nowrap}
|
|
1973
2009
|
.theone-entry-sub{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
|
|
1974
2010
|
.theone-opening{padding:32px;color:var(--dsw-alias-label-primary);font:inherit}
|
|
1975
2011
|
.theone-opening button{padding:8px 16px;font:inherit;color:inherit;background:var(--dsw-alias-interactive-bg-hover);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer}
|
|
@@ -1986,7 +2022,7 @@ var composerCss = `
|
|
|
1986
2022
|
.theone-bg:disabled{opacity:.6;cursor:default}
|
|
1987
2023
|
.theone-bg-caption,.theone-bg-chevron{color:var(--dsw-alias-label-secondary)}
|
|
1988
2024
|
.theone-bg-text{overflow:hidden;text-overflow:ellipsis;display:var(--dsh-composer-model-text-display,inline)}
|
|
1989
|
-
.theone-bg-chevron{
|
|
2025
|
+
.theone-bg-caption,.theone-bg-chevron{display:inline-flex}
|
|
1990
2026
|
.theone-bg[data-failed=true] .theone-bg-caption{color:#d9480f}
|
|
1991
2027
|
.theone-bg-menu{position:fixed;z-index:10000;width:280px;max-height:min(420px,70vh);display:flex;flex-direction:column;padding:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);box-shadow:0 10px 32px #0003;font:inherit;font-size:14px;box-sizing:border-box}
|
|
1992
2028
|
.theone-bg-heading{padding:8px 10px 6px;font-size:12px;color:var(--dsw-alias-label-secondary)}
|
package/dist/update.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ export interface UpdateStatus {
|
|
|
12
12
|
source: UpdateSource;
|
|
13
13
|
/** installing → reloading (TheOne restarts itself; DSH keeps running), restart (applies after DSH restarts) or failed. */
|
|
14
14
|
state?: 'installing' | 'reloading' | 'restart' | 'failed';
|
|
15
|
-
/** Why it failed: MINIMUM_RELEASE_AGE, NETWORK, or the plugin manager's own code. */
|
|
15
|
+
/** Why it failed: MINIMUM_RELEASE_AGE, OTHER_RELEASE_AGE (another package is too new), NETWORK, or the plugin manager's own code. */
|
|
16
16
|
error?: string;
|
|
17
|
+
/** pnpm's own words about the failure, or the packages it refused. */
|
|
18
|
+
detail?: string;
|
|
17
19
|
/** A newer npm version that pnpm will accept only once it is a day old, and when that is. */
|
|
18
20
|
waiting?: {
|
|
19
21
|
version: string;
|
|
@@ -31,6 +33,11 @@ export declare function releaseAgeExemptions(text: string): string[];
|
|
|
31
33
|
* kept as it was. Throws for a layout it does not recognise rather than guess.
|
|
32
34
|
*/
|
|
33
35
|
export declare function withReleaseAgeExemption(text: string, name?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* The packages pnpm refused for being too new, as `name@version`, read from its output
|
|
38
|
+
* ("dsh-theone@0.3.13 was published at …").
|
|
39
|
+
*/
|
|
40
|
+
export declare function releaseAgeViolations(output: string): string[];
|
|
34
41
|
/**
|
|
35
42
|
* pnpm, which DSH installs plugins with, refuses npm versions published less than a day ago
|
|
36
43
|
* (minimumReleaseAge, a supply-chain safeguard). Updates from npm wait until then.
|
|
@@ -66,6 +73,7 @@ export declare class Updater {
|
|
|
66
73
|
private checked?;
|
|
67
74
|
private state?;
|
|
68
75
|
private error?;
|
|
76
|
+
private detail?;
|
|
69
77
|
private pending?;
|
|
70
78
|
/**
|
|
71
79
|
* @param workspace - this DSH profile's pnpm-workspace.yaml, where pnpm reads the release-age
|
|
@@ -76,6 +84,8 @@ export declare class Updater {
|
|
|
76
84
|
get exempt(): boolean;
|
|
77
85
|
/** Exempt TheOne, and only TheOne, from pnpm's release-age rule in this profile. */
|
|
78
86
|
allowFresh(): void;
|
|
87
|
+
/** Add exact `name@version` exemptions too, the form pnpm writes itself; returns whether the file changed. */
|
|
88
|
+
private exemptExact;
|
|
79
89
|
get source(): UpdateSource;
|
|
80
90
|
/** The latest known status; checks again at most every six hours (or now, when forced). */
|
|
81
91
|
status(force?: boolean): Promise<UpdateStatus>;
|
package/dist/update.js
CHANGED
|
@@ -43,6 +43,13 @@ export function withReleaseAgeExemption(text, name = PACKAGE_NAME) {
|
|
|
43
43
|
lines.splice(end, 0, `${indent}- ${name}`);
|
|
44
44
|
return lines.join(newline);
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The packages pnpm refused for being too new, as `name@version`, read from its output
|
|
48
|
+
* ("dsh-theone@0.3.13 was published at …").
|
|
49
|
+
*/
|
|
50
|
+
export function releaseAgeViolations(output) {
|
|
51
|
+
return [...new Set([...output.matchAll(/^\s*((?:@[\w.-]+\/)?[\w.-]+@\d[\w.+-]*) was published at/gm)].map(match => match[1]))];
|
|
52
|
+
}
|
|
46
53
|
/**
|
|
47
54
|
* pnpm, which DSH installs plugins with, refuses npm versions published less than a day ago
|
|
48
55
|
* (minimumReleaseAge, a supply-chain safeguard). Updates from npm wait until then.
|
|
@@ -75,6 +82,8 @@ export function installSource(spec) {
|
|
|
75
82
|
return 'other';
|
|
76
83
|
}
|
|
77
84
|
const HOUR = 3600000;
|
|
85
|
+
const installed = (result) => result.application === 'applied' || result.application === 'restart-required';
|
|
86
|
+
const outputOf = (result) => `${result.packageResult?.output ?? ''}\n${result.error?.message ?? ''}`;
|
|
78
87
|
/** Checks for a newer TheOne and installs it through DSH's own plugin manager. */
|
|
79
88
|
export class Updater {
|
|
80
89
|
current;
|
|
@@ -85,6 +94,7 @@ export class Updater {
|
|
|
85
94
|
checked;
|
|
86
95
|
state;
|
|
87
96
|
error;
|
|
97
|
+
detail;
|
|
88
98
|
pending;
|
|
89
99
|
/**
|
|
90
100
|
* @param workspace - this DSH profile's pnpm-workspace.yaml, where pnpm reads the release-age
|
|
@@ -130,6 +140,25 @@ export class Updater {
|
|
|
130
140
|
if (this.state === 'failed') {
|
|
131
141
|
this.state = undefined;
|
|
132
142
|
this.error = undefined;
|
|
143
|
+
this.detail = undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Add exact `name@version` exemptions too, the form pnpm writes itself; returns whether the file changed. */
|
|
147
|
+
exemptExact(entries) {
|
|
148
|
+
const file = this.workspace();
|
|
149
|
+
if (!file)
|
|
150
|
+
return false;
|
|
151
|
+
try {
|
|
152
|
+
const text = readFileSync(file, 'utf8');
|
|
153
|
+
const next = entries.reduce((current, entry) => withReleaseAgeExemption(current, entry), text);
|
|
154
|
+
if (next === text)
|
|
155
|
+
return false;
|
|
156
|
+
writeFileSync(`${file}.theone-tmp`, next, { mode: 0o600 });
|
|
157
|
+
renameSync(`${file}.theone-tmp`, file);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false;
|
|
133
162
|
}
|
|
134
163
|
}
|
|
135
164
|
get source() { return installSource(this.spec()); }
|
|
@@ -155,9 +184,19 @@ export class Updater {
|
|
|
155
184
|
const spec = this.source === 'github' ? `github:${REPOSITORY}#${this.checked.sha}` : `${PACKAGE_NAME}@${status.latest}`;
|
|
156
185
|
this.state = 'installing';
|
|
157
186
|
this.error = undefined;
|
|
187
|
+
this.detail = undefined;
|
|
158
188
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
189
|
+
let result = await installer.installBundle(spec, { enabled: true });
|
|
190
|
+
let output = outputOf(result);
|
|
191
|
+
// pnpm also checks the lockfile, where the installed TheOne may itself be under a day old. If an
|
|
192
|
+
// exempt TheOne is still refused, exempt the exact versions involved, as pnpm does, and try once more.
|
|
193
|
+
const refused = /MINIMUM_RELEASE_AGE/.test(output) ? releaseAgeViolations(output) : [];
|
|
194
|
+
if (!installed(result) && this.source === 'npm' && this.exempt && refused.length && refused.every(entry => entry.startsWith(`${PACKAGE_NAME}@`))
|
|
195
|
+
&& this.exemptExact([...refused, `${PACKAGE_NAME}@${this.current}`, `${PACKAGE_NAME}@${status.latest}`])) {
|
|
196
|
+
result = await installer.installBundle(spec, { enabled: true });
|
|
197
|
+
output = outputOf(result);
|
|
198
|
+
}
|
|
199
|
+
if (installed(result)) {
|
|
161
200
|
this.state = reload ? 'reloading' : 'restart';
|
|
162
201
|
reload?.(result.bundle ?? PACKAGE_NAME);
|
|
163
202
|
}
|
|
@@ -165,10 +204,12 @@ export class Updater {
|
|
|
165
204
|
this.state = 'failed';
|
|
166
205
|
const error = result.error;
|
|
167
206
|
// pnpm's own reasons are clearer than the plugin manager's generic failure.
|
|
168
|
-
const
|
|
169
|
-
|
|
207
|
+
const violations = /MINIMUM_RELEASE_AGE/.test(output) ? releaseAgeViolations(output) : [];
|
|
208
|
+
const others = violations.filter(entry => !entry.startsWith(`${PACKAGE_NAME}@`));
|
|
209
|
+
this.error = others.length ? 'OTHER_RELEASE_AGE' : /MINIMUM_RELEASE_AGE/.test(output) ? 'MINIMUM_RELEASE_AGE'
|
|
170
210
|
: /UND_ERR|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ERR_PNPM_META_FETCH_FAIL|network/i.test(output) ? 'NETWORK'
|
|
171
211
|
: String(error?.code ?? error?.message ?? result.application).slice(0, 120);
|
|
212
|
+
this.detail = (others.length ? others.join(', ') : output.match(/ERR_PNPM_\w+[^\n]*(?:\n[^\n]+)?/)?.[0] ?? '').trim().slice(0, 300) || undefined;
|
|
172
213
|
}
|
|
173
214
|
}
|
|
174
215
|
catch (error) {
|
|
@@ -188,7 +229,8 @@ export class Updater {
|
|
|
188
229
|
return { current: this.current, ...(latest ? { latest } : {}), available, source, ...waiting,
|
|
189
230
|
...(source === 'npm' ? { exempt, canExempt: !!this.workspace() } : {}),
|
|
190
231
|
installable: available && (source === 'npm' || (source === 'github' && !!this.checked?.sha)),
|
|
191
|
-
...(this.state ? { state: this.state } : {}), ...(this.error ?? this.checked?.error ? { error: this.error ?? this.checked?.error } : {})
|
|
232
|
+
...(this.state ? { state: this.state } : {}), ...(this.error ?? this.checked?.error ? { error: this.error ?? this.checked?.error } : {}),
|
|
233
|
+
...(this.detail ? { detail: this.detail } : {}) };
|
|
192
234
|
}
|
|
193
235
|
async check() {
|
|
194
236
|
const signal = AbortSignal.timeout(10000);
|