@dsh-workbench/core 1.1.14-beta.1 → 1.1.14
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 +1 -1
- package/bin/dsh-workbench +6 -52
- package/client.js +59 -16
- package/cordis.patch.yml +5 -6
- package/dist/client/client.body.js +59 -16
- package/dist/client/index.js +59 -16
- package/dist/host/index.js +1665 -623
- package/dist/types/app/layouts/env-gate/gate-text.d.ts +2 -3
- package/dist/types/app/units/manifests.d.ts +2 -0
- package/dist/types/kernel/host/mount-units.d.ts +2 -1
- package/dist/types/modules/panels/model/diy-browser-act.d.ts +3 -3
- package/dist/types/modules/panels/model/diy-browser-clip.d.ts +14 -0
- package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -14
- package/dist/types/modules/panels/model/diy-browser.d.ts +6 -6
- package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
- package/dist/types/modules/panels/model/diy-record-open.d.ts +2 -2
- package/dist/types/modules/panels/model/diy-record.d.ts +12 -11
- package/dist/types/modules/panels/model/market-discover.d.ts +2 -2
- package/dist/types/modules/panels/model/market-follow-up.d.ts +2 -1
- package/dist/types/modules/panels/model/market-route.d.ts +2 -3
- package/dist/types/modules/panels/model/market-update-bundle.d.ts +2 -3
- package/dist/types/modules/panels/model/market-workbench-update.d.ts +2 -2
- package/dist/types/modules/panels/model/market.d.ts +2 -2
- package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -3
- package/dist/types/modules/panels/model/plugin-section.d.ts +3 -4
- package/dist/types/shared/api/host/bundle-routes.d.ts +3 -3
- package/dist/types/shared/api/host/diy-artifacts.d.ts +1 -2
- package/dist/types/shared/api/host/diy-components.d.ts +2 -2
- package/dist/types/shared/api/host/diy-release-staging.d.ts +6 -0
- package/dist/types/shared/api/host/diy.d.ts +1 -1
- package/dist/types/shared/api/host/git-exec.d.ts +1 -1
- package/dist/types/shared/api/host/git-process.d.ts +3 -0
- package/dist/types/shared/api/host/shipped-adapter-set.d.ts +4 -0
- package/dist/types/shared/api/host/workbench-seam.d.ts +3 -0
- package/package.json +3 -6
- package/scripts/seed-profile.sh +3 -3
- package/scripts/share-data-root.mjs +10 -13
- package/skills/workbench-module-diy/BUILD.md +38 -0
- package/skills/workbench-module-diy/SKILL.md +1 -0
- package/profile/sandbox.patch.yml +0 -28
package/README.md
CHANGED
|
@@ -203,7 +203,7 @@ HMR 热换插件(不刷新页面),宿主半侧按模块路径热重载。
|
|
|
203
203
|
| 装法、界面、适合谁 | [根 README](../../README.md) |
|
|
204
204
|
| 架构、两个运行时、槽位、数据通路、门禁 | [ARCHITECTURE.md](../../ARCHITECTURE.md) |
|
|
205
205
|
| 写一个模块 | [DIY 指引](skills/workbench-module-diy/SKILL.md) |
|
|
206
|
-
|
|
|
206
|
+
| 模块的构建与十四条判据 | [module-tools](../module-tools/README.md) |
|
|
207
207
|
| 共享包(契约、哑组件、宿主工具) | [shared](../shared/README.md) |
|
|
208
208
|
| 最小模块样本 | [module-hello](../module-hello/README.md) |
|
|
209
209
|
| 环境、门禁、提交 | [CONTRIBUTING.md](../../CONTRIBUTING.md) |
|
package/bin/dsh-workbench
CHANGED
|
@@ -403,58 +403,12 @@ fi
|
|
|
403
403
|
# 现在改成:**工作台自己的 patch 是唯一真相**。想要 web 的哪几行,去
|
|
404
404
|
# 设置 → 「配置同步」里勾(它读源 profile 的 cordis.patch.yml,按行搬运,
|
|
405
405
|
# 见 内部设计记录 与 ADR-0044)。启动器只负责把工作台起起来。
|
|
406
|
-
PATCH_ARGS=()
|
|
407
|
-
|
|
408
|
-
# ── 声明工作台运行时的**可写根**(执行缝的沙箱只放行 process.cwd())──────────
|
|
409
|
-
#
|
|
410
|
-
# 工作台自己要跑命令:启动体检自动装运行时、模块市场装/升插件、查源上新版。
|
|
411
|
-
# 这些命令走 dsh 的执行缝,而那条缝带**文件沙箱**;插件发起的调用**没有会话**,
|
|
412
|
-
# 回落到部署策略,出厂值是 `workspaceRoot: process.cwd()` —— 安装目标
|
|
413
|
-
# `$DSH_HOME/workbench/...` 不在启动目录下,被拒写(EPERM),"自动装"死在第一步,
|
|
414
|
-
# 界面只能退化成"让用户去终端抄一条命令"。这一层把可写根指到工作台运行时,
|
|
415
|
-
# 那一整棵树(隔离工具链 + npm 缓存 + 模块专属文件夹 + 市场 prefix)就都放行了。
|
|
416
406
|
#
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
#
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
PATCH_ARGS+=(--patch "$SANDBOX_PATCH")
|
|
423
|
-
else
|
|
424
|
-
echo "dsh-workbench: 提醒 —— 找不到 ${SANDBOX_PATCH},这次不声明可写根(自动装会失败)。" >&2
|
|
425
|
-
fi
|
|
426
|
-
|
|
427
|
-
# DIY 工坊的目录必须**每次启动都在**:`cordis.patch.yml` 的 hmr 行把它列成一个监听根
|
|
428
|
-
# (工坊里的模块是被 link 进 profile 的仓外目录,Node 解析到的是工坊里的真路径,
|
|
429
|
-
# 所以不把它列进去,模块重建 dist/host.js 就不热换 —— 而且**不报错**)。
|
|
430
|
-
# chokidar 对不存在的根收不到事件,所以这里保住它,不必等用户重跑 setup。
|
|
431
|
-
# 只建目录:骨架与依赖是 seed-profile.sh 第 6 步的事。
|
|
407
|
+
# 工坊骨架 / `dsh-wb-module` 入口 / DIY 指引软链 / 隔离运行时的稳定入口:**一个写口**,
|
|
408
|
+
# 全在宿主自举里(`runtime.seed()` → `scripts/seed-workshop.sh`),三条安装路径每次启动都跑它
|
|
409
|
+
# (独立实例 / `dsh web` / Desktop 插进任意 profile 是同一份实现)。启动器在这里**只保目录**:
|
|
410
|
+
# hmr 行把它当监听根,而 chokidar 对不存在的根收不到事件(模块重建 dist/host.js 就不热换,
|
|
411
|
+
# 而且不报错)。骨架与依赖在建 profile 那一步装(`seed-profile.sh` → `seed-workshop.sh` 完整档)。
|
|
432
412
|
mkdir -p "$WORKBENCH_HOME/diy/packages"
|
|
433
413
|
|
|
434
|
-
|
|
435
|
-
# 于是 agent 在工坊里(或任何目录里)都能直接叫 `dsh-wb-module`,不必记着 `pnpm exec` 前缀 ——
|
|
436
|
-
# 指令里那句"工坊里没有全局 dsh-wb-module"是**旧**状态,现在有一个我们自己的转发脚本。
|
|
437
|
-
bash "$REPO_DIR/scripts/seed-workshop.sh" entry || true
|
|
438
|
-
export PATH="$WORKBENCH_HOME/bin:$PATH"
|
|
439
|
-
|
|
440
|
-
# 骨架不在就**当场建**(不是只提醒):工坊看着"能用",可模块的 build / check 会把
|
|
441
|
-
# "不在 workspace 里"说成"声明写错了"——实测就是在这一步卡住过(干净机器上 seed 第 6 步没跑)。
|
|
442
|
-
# 提醒是对人说的,而先撞上它的是 agent:它读到的报错指着别的地方,于是开始自由发挥。
|
|
443
|
-
if [ ! -f "$WORKBENCH_HOME/diy/package.json" ]; then
|
|
444
|
-
bash "$REPO_DIR/scripts/seed-workshop.sh" || {
|
|
445
|
-
echo "dsh-workbench: 提醒 —— DIY 工坊的骨架没建起来($WORKBENCH_HOME/diy/package.json)。" >&2
|
|
446
|
-
echo " 跑一次 dsh-workbench setup;在那之前工坊里的模块 build / check 都会报错。" >&2
|
|
447
|
-
}
|
|
448
|
-
fi
|
|
449
|
-
|
|
450
|
-
# 工坊**自己带着** DIY 的指引(软链到随包出货的那份 `skills/workbench-module-diy/`)。
|
|
451
|
-
# 这条链**不在这里挂** —— 写口只有一处,在 `scripts/seed-workshop.sh` 的 `link_workshop_skills`
|
|
452
|
-
# (上面那次 `entry` 已经跑过它)。为什么收成一处:启动器与宿主自举都跑那个脚本,从前两处
|
|
453
|
-
# 各写一遍(这里一段 bash + 宿主 TS 的 `linkSkills`),目标虽一致,却是**同一件事两个实现** ——
|
|
454
|
-
# 哪天只有一边改了源或判据就会互相改指,而软链指到哪 skill 发现都**不报错**。
|
|
455
|
-
# 运行时的**稳定入口**不在这里建:它属于适配器(隔离包装随适配器出货),
|
|
456
|
-
# 由宿主自举在每次启动时写到自己的隔离根里 —— 写口只有那一处。
|
|
457
|
-
# ⚠️ 顺序有讲究:`--patch` 是 **dsh 本体**的选项,必须排在 `--port`(应用参数)**之前** ——
|
|
458
|
-
# 参数一旦进了应用的透传区(第一个应用自己的选项开始),后面的都直接交给应用,
|
|
459
|
-
# `--patch` 会被应用当成未知选项(实测:`--port` 在前时报 `unknown option '--patch'`)。
|
|
460
|
-
exec dsh "$PROFILE" ${PATCH_ARGS[@]+"${PATCH_ARGS[@]}"} --port "$PORT" ${DSH_ARGS[@]+"${DSH_ARGS[@]}"}
|
|
414
|
+
exec dsh "$PROFILE" --port "$PORT" ${DSH_ARGS[@]+"${DSH_ARGS[@]}"}
|
package/client.js
CHANGED
|
@@ -3595,9 +3595,8 @@ window.__ModuleLoader__.load({
|
|
|
3595
3595
|
"env.title": "Preparing the workbench",
|
|
3596
3596
|
"env.checking": "Getting the runtime ready…",
|
|
3597
3597
|
"env.installing": "Installing background tools — the first run takes a moment…",
|
|
3598
|
-
"env.installingToolchain": "Installing the workbench’s own
|
|
3598
|
+
"env.installingToolchain": "Installing the workbench’s own pnpm — the first run downloads it, so it can take a while.",
|
|
3599
3599
|
"env.failed": "One step away",
|
|
3600
|
-
"env.elapsed": "Waiting {seconds}s",
|
|
3601
3600
|
"env.stepFailed.install": "install failed",
|
|
3602
3601
|
"env.stepFailed.path": "installed but not found",
|
|
3603
3602
|
"env.stepFailed.shell": "cannot reach the command line",
|
|
@@ -3616,14 +3615,15 @@ window.__ModuleLoader__.load({
|
|
|
3616
3615
|
"env.fix.whyShell": "The workbench cannot reach the command line on this machine, so it cannot fetch data — you cannot install this from here; someone needs to fix the background environment.",
|
|
3617
3616
|
"env.fix.whyHost": "The workbench runs a few checks on the command line and collects what they return; this time the command went out and nothing came back. The tools themselves are most likely fine — reinstalling Node / npm / pnpm will not help.",
|
|
3618
3617
|
"env.fix.step": "Run this in a terminal, then hit Retry:",
|
|
3618
|
+
"env.fix.stepRetry": "Hit Retry to try again; if it still fails you can enter the workbench anyway and restart it once the install succeeds.",
|
|
3619
3619
|
"env.fix.stepPath": "Open a new terminal window (or make sure npm’s global bin directory is on PATH), then hit Retry.",
|
|
3620
3620
|
"env.fix.stepShell": "Restart the workbench (or dsh); if it keeps happening, the workbench still cannot reach the command line.",
|
|
3621
3621
|
"env.fix.stepHost": "Quit the workbench completely (including the tray icon), clear the dsh-* leftovers in the system temp folder, then open it again. If it keeps happening, update dsh.",
|
|
3622
3622
|
"env.fix.permission": "Permission denied: rerun with sudo, or point npm’s global directory at your home.",
|
|
3623
3623
|
"env.fix.network": "Network unreachable: check your network or proxy, then retry.",
|
|
3624
3624
|
"env.fix.details": "Details (for troubleshooting)",
|
|
3625
|
-
"env.log": "Detailed output",
|
|
3626
3625
|
"env.retry": "Retry",
|
|
3626
|
+
"env.failed.continue": "Enter workbench anyway",
|
|
3627
3627
|
"env.wake": "Open browser",
|
|
3628
3628
|
"env.wake.opening": "Opening the browser…",
|
|
3629
3629
|
"env.wake.browserOpen": "The browser is already running but the extension is not connected — check that it is enabled.",
|
|
@@ -4365,9 +4365,8 @@ window.__ModuleLoader__.load({
|
|
|
4365
4365
|
"env.title": "准备工作台",
|
|
4366
4366
|
"env.checking": "正在准备运行环境…",
|
|
4367
4367
|
"env.installing": "正在安装后台工具,第一次会慢一点…",
|
|
4368
|
-
"env.installingToolchain": "正在装工作台自己那份
|
|
4368
|
+
"env.installingToolchain": "正在装工作台自己那份 pnpm —— 第一次要下载,慢一点正常。",
|
|
4369
4369
|
"env.failed": "还差一步就能用了",
|
|
4370
|
-
"env.elapsed": "已等待 {seconds} 秒",
|
|
4371
4370
|
"env.stepFailed.install": "装不上",
|
|
4372
4371
|
"env.stepFailed.path": "装了但还找不到",
|
|
4373
4372
|
"env.stepFailed.shell": "连不上命令行",
|
|
@@ -4386,14 +4385,15 @@ window.__ModuleLoader__.load({
|
|
|
4386
4385
|
"env.fix.whyShell": "工作台拿不到这台机器的命令行,所以取不了数据 —— 这一步你装不了,需要有人处理后台环境。",
|
|
4387
4386
|
"env.fix.whyHost": "工作台要靠命令行跑几条检查,再把结果收回来;这次命令发出去了,结果一直没回来。工具本身多半是好的 —— 重装 Node / npm / pnpm 不会有帮助。",
|
|
4388
4387
|
"env.fix.step": "在终端里运行下面这条命令,装好之后点「重试」:",
|
|
4388
|
+
"env.fix.stepRetry": "点「重试」再装一次;还装不上也可以先进入工作台 —— 装好之后重启工作台就会用上。",
|
|
4389
4389
|
"env.fix.stepPath": "新开一个终端窗口(或确认 npm 的全局 bin 目录在 PATH 里),然后点「重试」。",
|
|
4390
4390
|
"env.fix.stepShell": "重启一次工作台(或 dsh);如果反复出现,说明工作台还是拿不到命令行。",
|
|
4391
4391
|
"env.fix.stepHost": "完全退出工作台(托盘图标也要退),清掉系统临时目录里 dsh- 开头的残留,再重新打开;反复出现就升级 dsh。",
|
|
4392
4392
|
"env.fix.permission": "权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。",
|
|
4393
4393
|
"env.fix.network": "网络不通:检查网络或代理设置后重试。",
|
|
4394
4394
|
"env.fix.details": "详细输出(排障用)",
|
|
4395
|
-
"env.log": "详细输出",
|
|
4396
4395
|
"env.retry": "重试",
|
|
4396
|
+
"env.failed.continue": "先进入工作台",
|
|
4397
4397
|
"env.wake": "打开浏览器",
|
|
4398
4398
|
"env.wake.opening": "正在打开浏览器…",
|
|
4399
4399
|
"env.wake.browserOpen": "浏览器已经开着,但扩展没连上 —— 检查扩展是否启用。",
|
|
@@ -13018,10 +13018,16 @@ window.__ModuleLoader__.load({
|
|
|
13018
13018
|
}
|
|
13019
13019
|
//#endregion
|
|
13020
13020
|
//#region src/app/units/manifests.ts
|
|
13021
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
13022
|
+
var DEFAULT_MAX_RETRIES = 30;
|
|
13021
13023
|
function createUnitManifestLoader(options = {}) {
|
|
13022
13024
|
let inFlight = false;
|
|
13023
13025
|
let queued = false;
|
|
13024
13026
|
let watching = false;
|
|
13027
|
+
let retries = 0;
|
|
13028
|
+
let timer = null;
|
|
13029
|
+
const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
13030
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
13025
13031
|
const subscribeFacts = options.subscribeFacts ?? subscribeFactRevision;
|
|
13026
13032
|
const load = async (registry) => {
|
|
13027
13033
|
if (inFlight) {
|
|
@@ -13029,11 +13035,23 @@ window.__ModuleLoader__.load({
|
|
|
13029
13035
|
return;
|
|
13030
13036
|
}
|
|
13031
13037
|
inFlight = true;
|
|
13038
|
+
let settled = false;
|
|
13032
13039
|
try {
|
|
13033
13040
|
const response = await apiGet(UNIT_MANIFESTS_ROUTE).catch(() => null);
|
|
13034
|
-
if (response?.ok === true && Array.isArray(response.manifests))
|
|
13041
|
+
if (response?.ok === true && Array.isArray(response.manifests)) {
|
|
13042
|
+
registry.setManifests(response.manifests, Array.isArray(response.installable) ? response.installable : void 0);
|
|
13043
|
+
settled = !registry.refusals().some((item) => item.kind === "no-manifest");
|
|
13044
|
+
}
|
|
13035
13045
|
} finally {
|
|
13036
13046
|
inFlight = false;
|
|
13047
|
+
if (settled) retries = 0;
|
|
13048
|
+
else if (retries < maxRetries && timer === null) {
|
|
13049
|
+
retries += 1;
|
|
13050
|
+
timer = setTimeout(() => {
|
|
13051
|
+
timer = null;
|
|
13052
|
+
load(registry);
|
|
13053
|
+
}, retryDelayMs);
|
|
13054
|
+
}
|
|
13037
13055
|
if (queued) {
|
|
13038
13056
|
queued = false;
|
|
13039
13057
|
load(registry);
|
|
@@ -13043,7 +13061,10 @@ window.__ModuleLoader__.load({
|
|
|
13043
13061
|
return (registry) => {
|
|
13044
13062
|
if (!watching) {
|
|
13045
13063
|
watching = true;
|
|
13046
|
-
subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () =>
|
|
13064
|
+
subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () => {
|
|
13065
|
+
retries = 0;
|
|
13066
|
+
load(registry);
|
|
13067
|
+
});
|
|
13047
13068
|
}
|
|
13048
13069
|
load(registry);
|
|
13049
13070
|
};
|
|
@@ -13088,7 +13109,7 @@ window.__ModuleLoader__.load({
|
|
|
13088
13109
|
if (guidance === "host") return t("env.stepFailed.host");
|
|
13089
13110
|
return t("env.stepFailed.install");
|
|
13090
13111
|
}
|
|
13091
|
-
function guidanceTextOf(guidance, t) {
|
|
13112
|
+
function guidanceTextOf(guidance, t, hasCommand = true) {
|
|
13092
13113
|
if (guidance === "path") return {
|
|
13093
13114
|
why: t("env.fix.whyPath"),
|
|
13094
13115
|
step: t("env.fix.stepPath")
|
|
@@ -13103,7 +13124,7 @@ window.__ModuleLoader__.load({
|
|
|
13103
13124
|
};
|
|
13104
13125
|
return {
|
|
13105
13126
|
why: t("env.fix.why"),
|
|
13106
|
-
step: t("env.fix.step")
|
|
13127
|
+
step: hasCommand ? t("env.fix.step") : t("env.fix.stepRetry")
|
|
13107
13128
|
};
|
|
13108
13129
|
}
|
|
13109
13130
|
function stepDetailText(step, snapshot, t) {
|
|
@@ -13128,6 +13149,19 @@ window.__ModuleLoader__.load({
|
|
|
13128
13149
|
};
|
|
13129
13150
|
return { text: t("env.bridge.unknown") };
|
|
13130
13151
|
}
|
|
13152
|
+
function bridgeAdviceLines(issues, t, fixes) {
|
|
13153
|
+
const listed = issues.length === 0 ? ["unknown"] : issues;
|
|
13154
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13155
|
+
const out = [];
|
|
13156
|
+
for (const issue of listed) {
|
|
13157
|
+
const advice = bridgeAdvice(issue, t, fixes);
|
|
13158
|
+
const key = `${advice.text}|${advice.link?.href ?? ""}`;
|
|
13159
|
+
if (seen.has(key)) continue;
|
|
13160
|
+
seen.add(key);
|
|
13161
|
+
out.push(advice);
|
|
13162
|
+
}
|
|
13163
|
+
return out;
|
|
13164
|
+
}
|
|
13131
13165
|
//#endregion
|
|
13132
13166
|
//#region src/app/layouts/env-gate/gate-deadline.ts
|
|
13133
13167
|
function createGateDeadline(giveUpMs, expire) {
|
|
@@ -13288,8 +13322,10 @@ window.__ModuleLoader__.load({
|
|
|
13288
13322
|
const buttons = el("div", BootGate_module_default.buttons);
|
|
13289
13323
|
const retry = el("button", BootGate_module_default.button, t("env.retry"));
|
|
13290
13324
|
retry.setAttribute("type", "button");
|
|
13325
|
+
const failedSkip = el("button", BootGate_module_default.button, t("env.failed.continue"));
|
|
13326
|
+
failedSkip.setAttribute("type", "button");
|
|
13291
13327
|
const fixCommand = commandRow(t);
|
|
13292
|
-
buttons.append(retry);
|
|
13328
|
+
buttons.append(retry, failedSkip);
|
|
13293
13329
|
actions.append(fixTitle, fixWhy, fixStep, fixCommand.row, fixAdvice, buttons);
|
|
13294
13330
|
const bridge = el("div", BootGate_module_default.bridge);
|
|
13295
13331
|
bridge.hidden = true;
|
|
@@ -13333,6 +13369,8 @@ window.__ModuleLoader__.load({
|
|
|
13333
13369
|
let cancelled = false;
|
|
13334
13370
|
const forcePlan = createForcePlan();
|
|
13335
13371
|
let pinned = null;
|
|
13372
|
+
let lastSnapshot = null;
|
|
13373
|
+
let runtimeProfile;
|
|
13336
13374
|
function close() {
|
|
13337
13375
|
if (closed) return;
|
|
13338
13376
|
closed = true;
|
|
@@ -13389,6 +13427,7 @@ window.__ModuleLoader__.load({
|
|
|
13389
13427
|
return row;
|
|
13390
13428
|
}
|
|
13391
13429
|
function render(snapshot) {
|
|
13430
|
+
lastSnapshot = snapshot;
|
|
13392
13431
|
wakeNote.hidden = true;
|
|
13393
13432
|
if (bootHintShown && (snapshot.phase !== "idle" || snapshot.steps.length > 0)) {
|
|
13394
13433
|
bootHintShown = false;
|
|
@@ -13429,10 +13468,10 @@ window.__ModuleLoader__.load({
|
|
|
13429
13468
|
bridgeRetry.removeAttribute("disabled");
|
|
13430
13469
|
const commandText = snapshot.steps.find((step) => step.status === "failed" && step.command !== void 0)?.command;
|
|
13431
13470
|
const guidance = snapshot.guidance ?? "install";
|
|
13432
|
-
const
|
|
13471
|
+
const showCommand = guidance === "install" && commandText !== void 0;
|
|
13472
|
+
const advice = guidanceTextOf(guidance, t, showCommand);
|
|
13433
13473
|
fixWhy.textContent = advice.why;
|
|
13434
13474
|
fixStep.textContent = advice.step;
|
|
13435
|
-
const showCommand = guidance === "install" && commandText !== void 0;
|
|
13436
13475
|
fixCommand.set(showCommand ? commandText : void 0);
|
|
13437
13476
|
fixAdvice.textContent = !showCommand ? "" : snapshot.failure === "permission" ? t("env.fix.permission") : snapshot.failure === "network" ? t("env.fix.network") : "";
|
|
13438
13477
|
const bridgeStep = snapshot.steps.find((step) => step.id === "bridge");
|
|
@@ -13440,9 +13479,7 @@ window.__ModuleLoader__.load({
|
|
|
13440
13479
|
const bridgeBroken = bridgeIssues.length > 0 || bridgeStep !== void 0 && (bridgeStep.status === "warn" || bridgeStep.status === "failed");
|
|
13441
13480
|
bridge.hidden = !bridgeBroken;
|
|
13442
13481
|
if (bridgeBroken) {
|
|
13443
|
-
|
|
13444
|
-
bridgeLines.replaceChildren(...listed.map((issue) => {
|
|
13445
|
-
const advice = bridgeAdvice(issue, t, runtimeProfileValue()?.bridge);
|
|
13482
|
+
bridgeLines.replaceChildren(...bridgeAdviceLines(bridgeIssues, t, runtimeProfile?.bridge ?? runtimeProfileValue()?.bridge).map((advice) => {
|
|
13446
13483
|
const item = el("li", BootGate_module_default.bridgeLine, advice.text);
|
|
13447
13484
|
if (advice.link !== void 0) {
|
|
13448
13485
|
const link = document.createElement("a");
|
|
@@ -13524,6 +13561,7 @@ window.__ModuleLoader__.load({
|
|
|
13524
13561
|
wakeNote.hidden = false;
|
|
13525
13562
|
}
|
|
13526
13563
|
retry.addEventListener("click", retryNow);
|
|
13564
|
+
failedSkip.addEventListener("click", close);
|
|
13527
13565
|
bridgeWake.addEventListener("click", () => void wakeBrowserNow());
|
|
13528
13566
|
bridgeRetry.addEventListener("click", retryNow);
|
|
13529
13567
|
bridgeSkip.addEventListener("click", close);
|
|
@@ -13541,6 +13579,11 @@ window.__ModuleLoader__.load({
|
|
|
13541
13579
|
} });
|
|
13542
13580
|
if (strip !== null) overlay.append(strip);
|
|
13543
13581
|
}
|
|
13582
|
+
readRuntimeCapabilities().then((answer) => {
|
|
13583
|
+
runtimeProfile = answer.ok ? answer.runtimeProfile ?? void 0 : void 0;
|
|
13584
|
+
if (pinned !== null) render(pinned);
|
|
13585
|
+
else if (lastSnapshot !== null) render(lastSnapshot);
|
|
13586
|
+
}).catch(() => void 0);
|
|
13544
13587
|
poll();
|
|
13545
13588
|
return { close };
|
|
13546
13589
|
}
|
package/cordis.patch.yml
CHANGED
|
@@ -134,12 +134,11 @@
|
|
|
134
134
|
# `file-upload` 注销 resolver 赛跑 —— 启用当场把 session-controller 打成 failed,
|
|
135
135
|
# 之后每次新建会话都是 `gateway/service-unavailable`(连 toggle 都救不回来,只能重启)。
|
|
136
136
|
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
# (见 runtime-seed 的 coversWritableRoot)。
|
|
137
|
+
# 现在可写根由**产品代码自己声明**(`src/shared/api/host/workbench-shell.ts` 的
|
|
138
|
+
# `provideWorkbenchShell`:`mode: 'workspace-write'`、`workspaceRoot: dshHome()`),
|
|
139
|
+
# 既不在 bundle、也不在 profile、也不需要启动器的 `--patch`:三条安装路径
|
|
140
|
+
# (独立实例 / `dsh web` / Desktop 插件页)行为一致。
|
|
141
|
+
# 证据与 A/B:ADR-0037 的 2026-10-22 补记;宿主启动时会打一行 info 说明实际可写根。
|
|
143
142
|
|
|
144
143
|
- insert:
|
|
145
144
|
- id: workbench
|
|
@@ -3588,9 +3588,8 @@ var en = {
|
|
|
3588
3588
|
"env.title": "Preparing the workbench",
|
|
3589
3589
|
"env.checking": "Getting the runtime ready…",
|
|
3590
3590
|
"env.installing": "Installing background tools — the first run takes a moment…",
|
|
3591
|
-
"env.installingToolchain": "Installing the workbench’s own
|
|
3591
|
+
"env.installingToolchain": "Installing the workbench’s own pnpm — the first run downloads it, so it can take a while.",
|
|
3592
3592
|
"env.failed": "One step away",
|
|
3593
|
-
"env.elapsed": "Waiting {seconds}s",
|
|
3594
3593
|
"env.stepFailed.install": "install failed",
|
|
3595
3594
|
"env.stepFailed.path": "installed but not found",
|
|
3596
3595
|
"env.stepFailed.shell": "cannot reach the command line",
|
|
@@ -3609,14 +3608,15 @@ var en = {
|
|
|
3609
3608
|
"env.fix.whyShell": "The workbench cannot reach the command line on this machine, so it cannot fetch data — you cannot install this from here; someone needs to fix the background environment.",
|
|
3610
3609
|
"env.fix.whyHost": "The workbench runs a few checks on the command line and collects what they return; this time the command went out and nothing came back. The tools themselves are most likely fine — reinstalling Node / npm / pnpm will not help.",
|
|
3611
3610
|
"env.fix.step": "Run this in a terminal, then hit Retry:",
|
|
3611
|
+
"env.fix.stepRetry": "Hit Retry to try again; if it still fails you can enter the workbench anyway and restart it once the install succeeds.",
|
|
3612
3612
|
"env.fix.stepPath": "Open a new terminal window (or make sure npm’s global bin directory is on PATH), then hit Retry.",
|
|
3613
3613
|
"env.fix.stepShell": "Restart the workbench (or dsh); if it keeps happening, the workbench still cannot reach the command line.",
|
|
3614
3614
|
"env.fix.stepHost": "Quit the workbench completely (including the tray icon), clear the dsh-* leftovers in the system temp folder, then open it again. If it keeps happening, update dsh.",
|
|
3615
3615
|
"env.fix.permission": "Permission denied: rerun with sudo, or point npm’s global directory at your home.",
|
|
3616
3616
|
"env.fix.network": "Network unreachable: check your network or proxy, then retry.",
|
|
3617
3617
|
"env.fix.details": "Details (for troubleshooting)",
|
|
3618
|
-
"env.log": "Detailed output",
|
|
3619
3618
|
"env.retry": "Retry",
|
|
3619
|
+
"env.failed.continue": "Enter workbench anyway",
|
|
3620
3620
|
"env.wake": "Open browser",
|
|
3621
3621
|
"env.wake.opening": "Opening the browser…",
|
|
3622
3622
|
"env.wake.browserOpen": "The browser is already running but the extension is not connected — check that it is enabled.",
|
|
@@ -4358,9 +4358,8 @@ var dictionaries = {
|
|
|
4358
4358
|
"env.title": "准备工作台",
|
|
4359
4359
|
"env.checking": "正在准备运行环境…",
|
|
4360
4360
|
"env.installing": "正在安装后台工具,第一次会慢一点…",
|
|
4361
|
-
"env.installingToolchain": "正在装工作台自己那份
|
|
4361
|
+
"env.installingToolchain": "正在装工作台自己那份 pnpm —— 第一次要下载,慢一点正常。",
|
|
4362
4362
|
"env.failed": "还差一步就能用了",
|
|
4363
|
-
"env.elapsed": "已等待 {seconds} 秒",
|
|
4364
4363
|
"env.stepFailed.install": "装不上",
|
|
4365
4364
|
"env.stepFailed.path": "装了但还找不到",
|
|
4366
4365
|
"env.stepFailed.shell": "连不上命令行",
|
|
@@ -4379,14 +4378,15 @@ var dictionaries = {
|
|
|
4379
4378
|
"env.fix.whyShell": "工作台拿不到这台机器的命令行,所以取不了数据 —— 这一步你装不了,需要有人处理后台环境。",
|
|
4380
4379
|
"env.fix.whyHost": "工作台要靠命令行跑几条检查,再把结果收回来;这次命令发出去了,结果一直没回来。工具本身多半是好的 —— 重装 Node / npm / pnpm 不会有帮助。",
|
|
4381
4380
|
"env.fix.step": "在终端里运行下面这条命令,装好之后点「重试」:",
|
|
4381
|
+
"env.fix.stepRetry": "点「重试」再装一次;还装不上也可以先进入工作台 —— 装好之后重启工作台就会用上。",
|
|
4382
4382
|
"env.fix.stepPath": "新开一个终端窗口(或确认 npm 的全局 bin 目录在 PATH 里),然后点「重试」。",
|
|
4383
4383
|
"env.fix.stepShell": "重启一次工作台(或 dsh);如果反复出现,说明工作台还是拿不到命令行。",
|
|
4384
4384
|
"env.fix.stepHost": "完全退出工作台(托盘图标也要退),清掉系统临时目录里 dsh- 开头的残留,再重新打开;反复出现就升级 dsh。",
|
|
4385
4385
|
"env.fix.permission": "权限不够:在命令前加 sudo 重跑,或先把 npm 的全局目录配到你的用户目录。",
|
|
4386
4386
|
"env.fix.network": "网络不通:检查网络或代理设置后重试。",
|
|
4387
4387
|
"env.fix.details": "详细输出(排障用)",
|
|
4388
|
-
"env.log": "详细输出",
|
|
4389
4388
|
"env.retry": "重试",
|
|
4389
|
+
"env.failed.continue": "先进入工作台",
|
|
4390
4390
|
"env.wake": "打开浏览器",
|
|
4391
4391
|
"env.wake.opening": "正在打开浏览器…",
|
|
4392
4392
|
"env.wake.browserOpen": "浏览器已经开着,但扩展没连上 —— 检查扩展是否启用。",
|
|
@@ -13011,10 +13011,16 @@ function createClientUnitRegistry(options = {}) {
|
|
|
13011
13011
|
}
|
|
13012
13012
|
//#endregion
|
|
13013
13013
|
//#region src/app/units/manifests.ts
|
|
13014
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
13015
|
+
var DEFAULT_MAX_RETRIES = 30;
|
|
13014
13016
|
function createUnitManifestLoader(options = {}) {
|
|
13015
13017
|
let inFlight = false;
|
|
13016
13018
|
let queued = false;
|
|
13017
13019
|
let watching = false;
|
|
13020
|
+
let retries = 0;
|
|
13021
|
+
let timer = null;
|
|
13022
|
+
const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
13023
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
13018
13024
|
const subscribeFacts = options.subscribeFacts ?? subscribeFactRevision;
|
|
13019
13025
|
const load = async (registry) => {
|
|
13020
13026
|
if (inFlight) {
|
|
@@ -13022,11 +13028,23 @@ function createUnitManifestLoader(options = {}) {
|
|
|
13022
13028
|
return;
|
|
13023
13029
|
}
|
|
13024
13030
|
inFlight = true;
|
|
13031
|
+
let settled = false;
|
|
13025
13032
|
try {
|
|
13026
13033
|
const response = await apiGet(UNIT_MANIFESTS_ROUTE).catch(() => null);
|
|
13027
|
-
if (response?.ok === true && Array.isArray(response.manifests))
|
|
13034
|
+
if (response?.ok === true && Array.isArray(response.manifests)) {
|
|
13035
|
+
registry.setManifests(response.manifests, Array.isArray(response.installable) ? response.installable : void 0);
|
|
13036
|
+
settled = !registry.refusals().some((item) => item.kind === "no-manifest");
|
|
13037
|
+
}
|
|
13028
13038
|
} finally {
|
|
13029
13039
|
inFlight = false;
|
|
13040
|
+
if (settled) retries = 0;
|
|
13041
|
+
else if (retries < maxRetries && timer === null) {
|
|
13042
|
+
retries += 1;
|
|
13043
|
+
timer = setTimeout(() => {
|
|
13044
|
+
timer = null;
|
|
13045
|
+
load(registry);
|
|
13046
|
+
}, retryDelayMs);
|
|
13047
|
+
}
|
|
13030
13048
|
if (queued) {
|
|
13031
13049
|
queued = false;
|
|
13032
13050
|
load(registry);
|
|
@@ -13036,7 +13054,10 @@ function createUnitManifestLoader(options = {}) {
|
|
|
13036
13054
|
return (registry) => {
|
|
13037
13055
|
if (!watching) {
|
|
13038
13056
|
watching = true;
|
|
13039
|
-
subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () =>
|
|
13057
|
+
subscribeFacts(factOfRoute(UNIT_MANIFESTS_ROUTE), () => {
|
|
13058
|
+
retries = 0;
|
|
13059
|
+
load(registry);
|
|
13060
|
+
});
|
|
13040
13061
|
}
|
|
13041
13062
|
load(registry);
|
|
13042
13063
|
};
|
|
@@ -13081,7 +13102,7 @@ function failedStepText(guidance, t) {
|
|
|
13081
13102
|
if (guidance === "host") return t("env.stepFailed.host");
|
|
13082
13103
|
return t("env.stepFailed.install");
|
|
13083
13104
|
}
|
|
13084
|
-
function guidanceTextOf(guidance, t) {
|
|
13105
|
+
function guidanceTextOf(guidance, t, hasCommand = true) {
|
|
13085
13106
|
if (guidance === "path") return {
|
|
13086
13107
|
why: t("env.fix.whyPath"),
|
|
13087
13108
|
step: t("env.fix.stepPath")
|
|
@@ -13096,7 +13117,7 @@ function guidanceTextOf(guidance, t) {
|
|
|
13096
13117
|
};
|
|
13097
13118
|
return {
|
|
13098
13119
|
why: t("env.fix.why"),
|
|
13099
|
-
step: t("env.fix.step")
|
|
13120
|
+
step: hasCommand ? t("env.fix.step") : t("env.fix.stepRetry")
|
|
13100
13121
|
};
|
|
13101
13122
|
}
|
|
13102
13123
|
function stepDetailText(step, snapshot, t) {
|
|
@@ -13121,6 +13142,19 @@ function bridgeAdvice(issue, t, fixes) {
|
|
|
13121
13142
|
};
|
|
13122
13143
|
return { text: t("env.bridge.unknown") };
|
|
13123
13144
|
}
|
|
13145
|
+
function bridgeAdviceLines(issues, t, fixes) {
|
|
13146
|
+
const listed = issues.length === 0 ? ["unknown"] : issues;
|
|
13147
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13148
|
+
const out = [];
|
|
13149
|
+
for (const issue of listed) {
|
|
13150
|
+
const advice = bridgeAdvice(issue, t, fixes);
|
|
13151
|
+
const key = `${advice.text}|${advice.link?.href ?? ""}`;
|
|
13152
|
+
if (seen.has(key)) continue;
|
|
13153
|
+
seen.add(key);
|
|
13154
|
+
out.push(advice);
|
|
13155
|
+
}
|
|
13156
|
+
return out;
|
|
13157
|
+
}
|
|
13124
13158
|
//#endregion
|
|
13125
13159
|
//#region src/app/layouts/env-gate/gate-deadline.ts
|
|
13126
13160
|
function createGateDeadline(giveUpMs, expire) {
|
|
@@ -13281,8 +13315,10 @@ function createBootGate(ctx, options = {}) {
|
|
|
13281
13315
|
const buttons = el("div", BootGate_module_default.buttons);
|
|
13282
13316
|
const retry = el("button", BootGate_module_default.button, t("env.retry"));
|
|
13283
13317
|
retry.setAttribute("type", "button");
|
|
13318
|
+
const failedSkip = el("button", BootGate_module_default.button, t("env.failed.continue"));
|
|
13319
|
+
failedSkip.setAttribute("type", "button");
|
|
13284
13320
|
const fixCommand = commandRow(t);
|
|
13285
|
-
buttons.append(retry);
|
|
13321
|
+
buttons.append(retry, failedSkip);
|
|
13286
13322
|
actions.append(fixTitle, fixWhy, fixStep, fixCommand.row, fixAdvice, buttons);
|
|
13287
13323
|
const bridge = el("div", BootGate_module_default.bridge);
|
|
13288
13324
|
bridge.hidden = true;
|
|
@@ -13326,6 +13362,8 @@ function createBootGate(ctx, options = {}) {
|
|
|
13326
13362
|
let cancelled = false;
|
|
13327
13363
|
const forcePlan = createForcePlan();
|
|
13328
13364
|
let pinned = null;
|
|
13365
|
+
let lastSnapshot = null;
|
|
13366
|
+
let runtimeProfile;
|
|
13329
13367
|
function close() {
|
|
13330
13368
|
if (closed) return;
|
|
13331
13369
|
closed = true;
|
|
@@ -13382,6 +13420,7 @@ function createBootGate(ctx, options = {}) {
|
|
|
13382
13420
|
return row;
|
|
13383
13421
|
}
|
|
13384
13422
|
function render(snapshot) {
|
|
13423
|
+
lastSnapshot = snapshot;
|
|
13385
13424
|
wakeNote.hidden = true;
|
|
13386
13425
|
if (bootHintShown && (snapshot.phase !== "idle" || snapshot.steps.length > 0)) {
|
|
13387
13426
|
bootHintShown = false;
|
|
@@ -13422,10 +13461,10 @@ function createBootGate(ctx, options = {}) {
|
|
|
13422
13461
|
bridgeRetry.removeAttribute("disabled");
|
|
13423
13462
|
const commandText = snapshot.steps.find((step) => step.status === "failed" && step.command !== void 0)?.command;
|
|
13424
13463
|
const guidance = snapshot.guidance ?? "install";
|
|
13425
|
-
const
|
|
13464
|
+
const showCommand = guidance === "install" && commandText !== void 0;
|
|
13465
|
+
const advice = guidanceTextOf(guidance, t, showCommand);
|
|
13426
13466
|
fixWhy.textContent = advice.why;
|
|
13427
13467
|
fixStep.textContent = advice.step;
|
|
13428
|
-
const showCommand = guidance === "install" && commandText !== void 0;
|
|
13429
13468
|
fixCommand.set(showCommand ? commandText : void 0);
|
|
13430
13469
|
fixAdvice.textContent = !showCommand ? "" : snapshot.failure === "permission" ? t("env.fix.permission") : snapshot.failure === "network" ? t("env.fix.network") : "";
|
|
13431
13470
|
const bridgeStep = snapshot.steps.find((step) => step.id === "bridge");
|
|
@@ -13433,9 +13472,7 @@ function createBootGate(ctx, options = {}) {
|
|
|
13433
13472
|
const bridgeBroken = bridgeIssues.length > 0 || bridgeStep !== void 0 && (bridgeStep.status === "warn" || bridgeStep.status === "failed");
|
|
13434
13473
|
bridge.hidden = !bridgeBroken;
|
|
13435
13474
|
if (bridgeBroken) {
|
|
13436
|
-
|
|
13437
|
-
bridgeLines.replaceChildren(...listed.map((issue) => {
|
|
13438
|
-
const advice = bridgeAdvice(issue, t, runtimeProfileValue()?.bridge);
|
|
13475
|
+
bridgeLines.replaceChildren(...bridgeAdviceLines(bridgeIssues, t, runtimeProfile?.bridge ?? runtimeProfileValue()?.bridge).map((advice) => {
|
|
13439
13476
|
const item = el("li", BootGate_module_default.bridgeLine, advice.text);
|
|
13440
13477
|
if (advice.link !== void 0) {
|
|
13441
13478
|
const link = document.createElement("a");
|
|
@@ -13517,6 +13554,7 @@ function createBootGate(ctx, options = {}) {
|
|
|
13517
13554
|
wakeNote.hidden = false;
|
|
13518
13555
|
}
|
|
13519
13556
|
retry.addEventListener("click", retryNow);
|
|
13557
|
+
failedSkip.addEventListener("click", close);
|
|
13520
13558
|
bridgeWake.addEventListener("click", () => void wakeBrowserNow());
|
|
13521
13559
|
bridgeRetry.addEventListener("click", retryNow);
|
|
13522
13560
|
bridgeSkip.addEventListener("click", close);
|
|
@@ -13534,6 +13572,11 @@ function createBootGate(ctx, options = {}) {
|
|
|
13534
13572
|
} });
|
|
13535
13573
|
if (strip !== null) overlay.append(strip);
|
|
13536
13574
|
}
|
|
13575
|
+
readRuntimeCapabilities().then((answer) => {
|
|
13576
|
+
runtimeProfile = answer.ok ? answer.runtimeProfile ?? void 0 : void 0;
|
|
13577
|
+
if (pinned !== null) render(pinned);
|
|
13578
|
+
else if (lastSnapshot !== null) render(lastSnapshot);
|
|
13579
|
+
}).catch(() => void 0);
|
|
13537
13580
|
poll();
|
|
13538
13581
|
return { close };
|
|
13539
13582
|
}
|