@michengai/dsh-automation 0.1.32 → 0.1.34

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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  The five most recent published versions are listed below. Git tags and GitHub Releases now mirror these entries; historical sections retain links to their original release commits.
6
6
 
7
+ ## Unreleased
8
+
9
+ ## 0.1.34 - 2026-09-07
10
+
11
+ - Aligned the release workflow's pnpm version with the package-declared version so tag publishing can start its checks.
12
+
13
+ ## 0.1.33 - 2026-09-07
14
+
15
+ - Added independent in-product update checks with automatic updates when a verified DSH update service is available and a profile-specific manual fallback otherwise.
16
+ - Removed the update-button dependency on `react-dom/client` so the client can load on Hosts that do not register that module id.
17
+
7
18
  ## 0.1.32 — 2026-09-06
8
19
 
9
20
  - Removed the fixed unattended tool allowlist and background-shell ban so tool calls and background process management follow DSH. Retained automatic rejection of approval requests and task timeout cancellation.
@@ -4,6 +4,17 @@
4
4
 
5
5
  以下记录最近发布的五个版本。Git 标签与 GitHub Release 现已和这些条目同步;历史条目继续保留原始发布提交链接。
6
6
 
7
+ ## 未发布
8
+
9
+ ## 0.1.34 - 2026-09-07
10
+
11
+ - 将发布工作流的 pnpm 版本与包内声明对齐,使标签发布能够进入完整校验。
12
+
13
+ ## 0.1.33 - 2026-09-07
14
+
15
+ - 新增独立的插件内检查更新能力:仅在已验证的 DSH 更新服务可用时自动更新,其余环境提供与 Profile 对应的手工更新命令。
16
+ - 更新按钮图标不再依赖 `react-dom/client`,避免宿主未注册该模块 id 时客户端无法加载。
17
+
7
18
  ## 0.1.32 — 2026-09-06
8
19
 
9
20
  - 移除无人值守自动化的固定工具白名单和后台 shell 禁令,工具调用及后台进程管理统一遵循 DSH;保留需要审批即拒绝和任务超时取消约束。
package/README.md CHANGED
@@ -104,6 +104,10 @@ dsh --profile web --dump-config
104
104
 
105
105
  Restart DSH Web and hard-refresh the browser. Local installation reads and applies `cordis.patch.yml`; do not copy `lib` files manually.
106
106
 
107
+ ## Updates
108
+
109
+ The settings title shows the installed version and a **Check for updates** button. When a newer release is available, **Update automatically** runs only when the DSH CLI or Desktop update service is available; otherwise, the dialog provides a profile-specific manual command to copy and run.
110
+
107
111
  ## Usage
108
112
 
109
113
  Open **Settings → Scheduled Tasks**, then use the panel as follows:
package/README.zh-CN.md CHANGED
@@ -104,6 +104,10 @@ dsh --profile web --dump-config
104
104
 
105
105
  完成后重启 DSH Web 并硬刷新浏览器。`dsh plugin ... add .` 会自动读取并应用 `cordis.patch.yml`;不要手工复制 `lib` 文件。
106
106
 
107
+ ## 在线更新
108
+
109
+ 设置标题会显示当前版本和“检查更新”按钮。发现新版后,只有检测到 DSH CLI 或 Desktop 更新服务时才可使用“自动更新”;其他环境会在弹窗中提供可复制、与当前 Profile 对应的手工更新命令。
110
+
107
111
  ## 使用
108
112
 
109
113
  打开「设置 → 定时任务」,再按下表操作:
package/lib/client.js CHANGED
@@ -4223,9 +4223,325 @@ function openSettingsSection(labels, onSelected, onMissing) {
4223
4223
  schedule();
4224
4224
  }
4225
4225
 
4226
+ // src/client/plugin-update-ui.ts
4227
+ var UPDATE_HEADER = "x-michengai-plugin-update";
4228
+ var STYLE_ID2 = "michengai-plugin-update-ui";
4229
+ var CSS = `
4230
+ .mpi-version{margin-left:8px;color:var(--dsw-alias-label-tertiary,#9da1aa);font-family:inherit;font-size:12px;font-weight:500;line-height:18px;letter-spacing:0;white-space:nowrap;vertical-align:baseline}.mpi-check{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2,#4b4d52);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#b8bbc2);font:inherit;font-size:12px;font-weight:500;line-height:18px;white-space:nowrap;cursor:pointer}.mpi-check:hover{background:var(--dsw-alias-interactive-bg-hover,#3a3b3f);color:var(--dsw-alias-label-primary,#fff)}.mpi-check:focus-visible,.mpi-action:focus-visible,.mpi-dialog-close:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4f8cff);outline-offset:2px}.mpi-icon{display:inline-flex;flex:0 0 auto;width:16px;height:16px;align-items:center;justify-content:center;pointer-events:none}.mpi-icon svg{display:block;width:16px;height:16px}
4231
+ .mpi-overlay{position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(0,0,0,.62)}.mpi-dialog{position:relative;box-sizing:border-box;width:min(680px,100%);max-height:calc(100vh - 48px);overflow:auto;border:1px solid var(--dsw-alias-border-l2,#4b4d52);border-radius:14px;padding:22px;background:var(--dsw-alias-bg-layer-2,var(--dsw-specific-menu,#202124));color:var(--dsw-alias-label-primary,#fff);box-shadow:var(--dsw-shadow-lv3,0 16px 48px rgba(0,0,0,.24));font-family:inherit}.mpi-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.mpi-dialog h2{margin:0;font-size:18px;line-height:26px}.mpi-dialog-close{display:inline-flex;flex:0 0 28px;width:28px;height:28px;align-items:center;justify-content:center;padding:0;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary,#b8bbc2);cursor:pointer}.mpi-dialog-close:hover{background:var(--dsw-alias-interactive-bg-hover,#3a3b3f);color:var(--dsw-alias-label-primary,#fff)}.mpi-intro{margin:8px 0 18px;color:var(--dsw-alias-label-secondary,#c2c4ca);font-size:13px;line-height:20px}.mpi-meta{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 18px;margin:0 0 16px;font-size:12px;line-height:18px}.mpi-meta dt{color:var(--dsw-alias-label-secondary,#c2c4ca)}.mpi-meta dd{margin:0;font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.mpi-status{margin:0 0 18px;border-radius:7px;padding:12px 14px;background:var(--dsw-alias-bg-layer-3,var(--dsw-specific-menu-item-hover,#252527));font-size:13px;font-weight:600;line-height:20px}.mpi-status[data-kind=error]{color:var(--dsw-alias-state-error-primary,#ff6464)}.mpi-status[data-kind=success]{color:var(--dsw-alias-state-success-primary,#36d67a)}.mpi-manual{border-top:1px solid var(--dsw-alias-border-l2,#4b4d52);padding-top:16px}.mpi-manual h3{margin:0 0 6px;font-size:14px;line-height:20px}.mpi-manual p{margin:0 0 10px;color:var(--dsw-alias-label-secondary,#c2c4ca);font-size:12px;line-height:18px}.mpi-command{display:flex;align-items:center;gap:8px;border:1px solid var(--dsw-alias-border-l2,#4b4d52);border-radius:7px;padding:10px;background:var(--dsw-alias-bg-layer-3,var(--dsw-specific-menu-item-hover,#252527))}.mpi-command code{min-width:0;flex:1;overflow:auto;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;line-height:18px;white-space:nowrap}.mpi-actions{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:18px}.mpi-actions-group{display:flex;gap:8px}.mpi-action{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;border:1px solid var(--dsw-alias-border-l2,#4b4d52);border-radius:7px;padding:6px 10px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}.mpi-action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#414247)}.mpi-action:disabled{cursor:not-allowed;opacity:.55}.mpi-primary{border-color:var(--dsw-alias-state-business-primary,#4f8cff);background:var(--dsw-alias-state-business-primary,#4f8cff);color:#fff}.mpi-progress{height:4px;margin-top:10px;overflow:hidden;border-radius:99px;background:var(--dsw-alias-border-l2,#4b4d52)}.mpi-progress::after{display:block;width:32%;height:100%;background:var(--dsw-alias-state-business-primary,#4f8cff);content:'';animation:mpi-wave 1.15s ease-in-out infinite}@keyframes mpi-wave{from{transform:translateX(-110%)}to{transform:translateX(330%)}}@media(max-width:560px){.mpi-overlay{padding:10px}.mpi-dialog{max-height:calc(100vh - 20px);padding:16px}.mpi-actions{align-items:stretch}.mpi-actions-group{justify-content:flex-end;flex-wrap:wrap}.mpi-meta{grid-template-columns:1fr;gap:2px}.mpi-meta dd{margin-bottom:6px}}
4232
+ `;
4233
+ var ZH = {
4234
+ check: "\u68C0\u67E5\u66F4\u65B0",
4235
+ update: "\u66F4\u65B0",
4236
+ close: "\u5173\u95ED",
4237
+ recheck: "\u91CD\u65B0\u68C0\u67E5",
4238
+ auto: "\u81EA\u52A8\u66F4\u65B0",
4239
+ updating: "\u6B63\u5728\u66F4\u65B0\u2026",
4240
+ copy: "\u590D\u5236\u547D\u4EE4",
4241
+ copied: "\u5DF2\u590D\u5236",
4242
+ copyFailed: "\u590D\u5236\u5931\u8D25",
4243
+ checking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026",
4244
+ latest: "\u5DF2\u662F\u6700\u65B0\u7248\u672C",
4245
+ found: "\u53D1\u73B0\u65B0\u7248\u672C",
4246
+ failed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
4247
+ current: "\u8FD0\u884C\u7248\u672C",
4248
+ latestLabel: "\u6700\u65B0\u7248\u672C",
4249
+ profile: "\u76EE\u6807 profile",
4250
+ unknown: "\u672A\u77E5",
4251
+ manual: "\u624B\u5DE5\u66F4\u65B0",
4252
+ manualHint: "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u65F6\uFF0C\u53EF\u5728\u5F53\u524D DSH \u7EC8\u7AEF\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\uFF0C\u5B8C\u6210\u540E\u91CD\u542F DSH Web\u3002",
4253
+ intro: "\u4EC5\u68C0\u67E5\u5E76\u66F4\u65B0\u5F53\u524D\u63D2\u4EF6\uFF0C\u4E0D\u4F1A\u8054\u52A8\u5B89\u88C5\u5176\u4ED6\u63D2\u4EF6\u3002",
4254
+ restart: "\u66F4\u65B0\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F DSH Web\u3002",
4255
+ restarting: "\u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F DSH Desktop\u2026",
4256
+ unavailable: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\uFF0C\u8BF7\u4F7F\u7528\u624B\u5DE5\u66F4\u65B0\u547D\u4EE4\u3002"
4257
+ };
4258
+ var EN = {
4259
+ check: "Check for updates",
4260
+ update: "Update",
4261
+ close: "Close",
4262
+ recheck: "Check again",
4263
+ auto: "Update automatically",
4264
+ updating: "Updating\u2026",
4265
+ copy: "Copy command",
4266
+ copied: "Copied",
4267
+ copyFailed: "Copy failed",
4268
+ checking: "Checking for updates\u2026",
4269
+ latest: "You are up to date",
4270
+ found: "New version available",
4271
+ failed: "Could not check for updates. Try again later.",
4272
+ current: "Running version",
4273
+ latestLabel: "Latest version",
4274
+ profile: "Target profile",
4275
+ unknown: "Unknown",
4276
+ manual: "Manual update",
4277
+ manualHint: "If automatic update fails, run this command in the current DSH terminal, then restart DSH Web.",
4278
+ intro: "Only this plugin is checked and updated. Other plugins are not changed.",
4279
+ restart: "Update complete. Restart DSH Web.",
4280
+ restarting: "Update complete. Restarting DSH Desktop\u2026",
4281
+ unavailable: "Automatic update is unavailable. Use the manual command."
4282
+ };
4283
+ function strings() {
4284
+ const lang = document.documentElement.lang.toLowerCase();
4285
+ const settings = document.querySelector('[role="dialog"]')?.textContent ?? "";
4286
+ return lang.startsWith("en") || settings.includes("Settings") && !settings.includes("\u8BBE\u7F6E") ? EN : ZH;
4287
+ }
4288
+ function ensureStyle() {
4289
+ if (document.getElementById(STYLE_ID2) !== null) return;
4290
+ const style = document.createElement("style");
4291
+ style.id = STYLE_ID2;
4292
+ style.textContent = CSS;
4293
+ (document.head ?? document.documentElement).append(style);
4294
+ }
4295
+ function validPayload(value) {
4296
+ if (value === null || typeof value !== "object") return false;
4297
+ const item = value;
4298
+ return typeof item.packageName === "string" && typeof item.currentVersion === "string" && typeof item.updateAvailable === "boolean" && typeof item.profileName === "string" && typeof item.canAutoUpdate === "boolean" && typeof item.latestCheckFailed === "boolean" && (item.latestVersion === void 0 || typeof item.latestVersion === "string");
4299
+ }
4300
+ async function requestStatus(endpoint, method, signal) {
4301
+ const signalOption = signal === void 0 ? {} : { signal };
4302
+ const response = await fetch(endpoint, method === "GET" ? { cache: "no-store", ...signalOption } : {
4303
+ method: "POST",
4304
+ headers: { "content-type": "application/json", [UPDATE_HEADER]: "1" },
4305
+ body: "{}",
4306
+ ...signalOption
4307
+ });
4308
+ const value = await response.json();
4309
+ if (!response.ok || !validPayload(value)) throw new Error(typeof value.error === "string" ? value.error : strings().failed);
4310
+ return value;
4311
+ }
4312
+ function manualPluginUpdateCommand(profileName, packageName, version) {
4313
+ const profile = profileName.trim() === "" ? "" : ` --profile ${profileName.trim()}`;
4314
+ return `dsh plugin${profile} add ${packageName}@${version} --registry=https://registry.npmjs.org/`;
4315
+ }
4316
+ function handlePluginUpdateEscape(event, close) {
4317
+ if (event.key !== "Escape") return false;
4318
+ event.preventDefault();
4319
+ event.stopPropagation();
4320
+ event.stopImmediatePropagation();
4321
+ close();
4322
+ return true;
4323
+ }
4324
+ function observePluginUpdate(options) {
4325
+ if (typeof document === "undefined" || document.body === null) return () => {
4326
+ };
4327
+ ensureStyle();
4328
+ const controller = new AbortController();
4329
+ let payload;
4330
+ let overlay;
4331
+ let frame;
4332
+ const setButtonContent = (button, label, iconName) => {
4333
+ const icon = options.createIcon(iconName);
4334
+ icon.classList.add("mpi-icon");
4335
+ icon.setAttribute("aria-hidden", "true");
4336
+ const text = document.createElement("span");
4337
+ text.dataset.mpiLabel = "";
4338
+ text.textContent = label;
4339
+ button.replaceChildren(icon, text);
4340
+ };
4341
+ const setButtonLabel = (button, label) => {
4342
+ const text = button.querySelector("[data-mpi-label]");
4343
+ if (text === null) button.textContent = label;
4344
+ else text.textContent = label;
4345
+ };
4346
+ const applyControls = () => {
4347
+ const row = document.querySelector(options.titleRowSelector);
4348
+ if (row === null) return;
4349
+ const heading = row.querySelector("h1,h2");
4350
+ if (heading !== null && payload !== void 0) {
4351
+ let version = heading.querySelector(`.mpi-version[data-package="${options.packageName}"]`);
4352
+ if (version === null) {
4353
+ version = document.createElement("span");
4354
+ version.className = "mpi-version";
4355
+ version.dataset.package = options.packageName;
4356
+ heading.append(version);
4357
+ }
4358
+ const versionLabel = `v${payload.currentVersion}`;
4359
+ if (version.textContent !== versionLabel) version.textContent = versionLabel;
4360
+ }
4361
+ const links = row.querySelector(options.linksSelector);
4362
+ if (links === null || links.querySelector(`[data-mpi-check="${options.packageName}"]`) !== null) return;
4363
+ const button = document.createElement("button");
4364
+ button.type = "button";
4365
+ button.className = "mpi-check";
4366
+ button.dataset.mpiCheck = options.packageName;
4367
+ setButtonContent(button, strings().check, "refresh");
4368
+ button.addEventListener("click", openDialog);
4369
+ links.append(button);
4370
+ };
4371
+ const load = async () => {
4372
+ payload = await requestStatus(options.endpoint, "GET", controller.signal);
4373
+ applyControls();
4374
+ return payload;
4375
+ };
4376
+ const closeDialog = () => {
4377
+ overlay?.remove();
4378
+ overlay = void 0;
4379
+ };
4380
+ function openDialog() {
4381
+ closeDialog();
4382
+ const text = strings();
4383
+ overlay = document.createElement("div");
4384
+ overlay.className = "mpi-overlay";
4385
+ const dialog = document.createElement("section");
4386
+ dialog.className = "mpi-dialog";
4387
+ dialog.setAttribute("role", "dialog");
4388
+ dialog.setAttribute("aria-modal", "true");
4389
+ dialog.innerHTML = `<header class="mpi-head"><h2></h2><button type="button" class="mpi-dialog-close" data-action="close"></button></header><p class="mpi-intro"></p><dl class="mpi-meta"><dt></dt><dd data-role="current"></dd><dt></dt><dd data-role="latest"></dd><dt></dt><dd data-role="profile"></dd></dl><div class="mpi-status" role="status"></div><div class="mpi-progress" hidden></div><section class="mpi-manual"><h3></h3><p></p><div class="mpi-command"><code></code><button type="button" class="mpi-action" data-action="copy"></button></div></section><footer class="mpi-actions"><div class="mpi-actions-group"><button type="button" class="mpi-action" data-action="check"></button><button type="button" class="mpi-action mpi-primary" data-action="update"></button></div></footer>`;
4390
+ const name2 = document.documentElement.lang.toLowerCase().startsWith("en") ? options.enName : options.zhName;
4391
+ dialog.querySelector("h2").textContent = `${name2} ${text.update}`;
4392
+ dialog.querySelector(".mpi-intro").textContent = text.intro;
4393
+ const terms = dialog.querySelectorAll("dt");
4394
+ terms[0].textContent = text.current;
4395
+ terms[1].textContent = text.latestLabel;
4396
+ terms[2].textContent = text.profile;
4397
+ dialog.querySelector(".mpi-manual h3").textContent = text.manual;
4398
+ dialog.querySelector(".mpi-manual p").textContent = text.manualHint;
4399
+ const status = dialog.querySelector(".mpi-status");
4400
+ const progress = dialog.querySelector(".mpi-progress");
4401
+ const command = dialog.querySelector(".mpi-command code");
4402
+ const close = dialog.querySelector("[data-action=close]");
4403
+ const check = dialog.querySelector("[data-action=check]");
4404
+ const update = dialog.querySelector("[data-action=update]");
4405
+ const copy = dialog.querySelector("[data-action=copy]");
4406
+ setButtonContent(close, text.close, "close");
4407
+ close.querySelector("[data-mpi-label]")?.remove();
4408
+ close.setAttribute("aria-label", text.close);
4409
+ close.title = text.close;
4410
+ setButtonContent(check, text.recheck, "refresh");
4411
+ setButtonContent(update, text.auto, "download");
4412
+ setButtonContent(copy, text.copy, "copy");
4413
+ let busy = false;
4414
+ const setMessage = (message, kind = "") => {
4415
+ status.textContent = message;
4416
+ status.dataset.kind = kind;
4417
+ };
4418
+ const setBusy = (value) => {
4419
+ busy = value;
4420
+ check.disabled = value;
4421
+ copy.disabled = value;
4422
+ update.disabled = value || payload?.canAutoUpdate !== true || payload.updateAvailable !== true;
4423
+ progress.hidden = !value;
4424
+ };
4425
+ const render = () => {
4426
+ dialog.querySelector("[data-role=current]").textContent = payload === void 0 ? text.unknown : `v${payload.currentVersion}`;
4427
+ dialog.querySelector("[data-role=latest]").textContent = payload?.latestVersion === void 0 ? text.unknown : `v${payload.latestVersion}`;
4428
+ dialog.querySelector("[data-role=profile]").textContent = payload?.profileName ?? text.unknown;
4429
+ command.textContent = manualPluginUpdateCommand(payload?.profileName ?? "", options.packageName, payload?.latestVersion ?? "latest");
4430
+ update.disabled = busy || payload?.canAutoUpdate !== true || payload.updateAvailable !== true;
4431
+ if (payload === void 0) setMessage(text.checking);
4432
+ else if (payload.latestCheckFailed) setMessage(text.failed, "error");
4433
+ else if (payload.updateAvailable) setMessage(`${text.found}: v${payload.latestVersion ?? text.unknown}`);
4434
+ else setMessage(text.latest, "success");
4435
+ if (payload !== void 0 && !payload.canAutoUpdate && payload.updateAvailable) setMessage(text.unavailable);
4436
+ };
4437
+ const checkNow = async () => {
4438
+ if (busy) return;
4439
+ setBusy(true);
4440
+ setMessage(text.checking);
4441
+ try {
4442
+ await load();
4443
+ setBusy(false);
4444
+ render();
4445
+ } catch (error) {
4446
+ setBusy(false);
4447
+ setMessage(error instanceof Error ? error.message : text.failed, "error");
4448
+ }
4449
+ };
4450
+ const updateNow = async () => {
4451
+ if (busy) return;
4452
+ setBusy(true);
4453
+ setButtonLabel(update, text.updating);
4454
+ setMessage(text.updating);
4455
+ try {
4456
+ payload = await requestStatus(options.endpoint, "POST", controller.signal);
4457
+ applyControls();
4458
+ render();
4459
+ setMessage(payload.autoReload === true ? text.restarting : text.restart, "success");
4460
+ } catch (error) {
4461
+ setMessage(error instanceof Error ? error.message : text.failed, "error");
4462
+ } finally {
4463
+ setButtonLabel(update, text.auto);
4464
+ setBusy(false);
4465
+ }
4466
+ };
4467
+ close.addEventListener("click", closeDialog);
4468
+ check.addEventListener("click", () => {
4469
+ void checkNow();
4470
+ });
4471
+ update.addEventListener("click", () => {
4472
+ void updateNow();
4473
+ });
4474
+ copy.addEventListener("click", () => {
4475
+ void navigator.clipboard?.writeText(command.textContent ?? "").then(() => {
4476
+ setButtonLabel(copy, text.copied);
4477
+ setTimeout(() => {
4478
+ setButtonLabel(copy, text.copy);
4479
+ }, 1400);
4480
+ }).catch(() => {
4481
+ setButtonLabel(copy, text.copyFailed);
4482
+ });
4483
+ });
4484
+ overlay.addEventListener("click", (event) => {
4485
+ if (event.target === overlay) closeDialog();
4486
+ });
4487
+ overlay.addEventListener("keydown", (event) => {
4488
+ handlePluginUpdateEscape(event, closeDialog);
4489
+ }, true);
4490
+ overlay.append(dialog);
4491
+ document.body.append(overlay);
4492
+ render();
4493
+ close.focus();
4494
+ void checkNow();
4495
+ }
4496
+ const observer = new MutationObserver(() => {
4497
+ if (frame !== void 0) return;
4498
+ frame = window.requestAnimationFrame(() => {
4499
+ frame = void 0;
4500
+ applyControls();
4501
+ });
4502
+ });
4503
+ observer.observe(document.body, { childList: true, subtree: true });
4504
+ applyControls();
4505
+ void load().catch(() => {
4506
+ });
4507
+ return () => {
4508
+ controller.abort();
4509
+ observer.disconnect();
4510
+ closeDialog();
4511
+ if (frame !== void 0) window.cancelAnimationFrame(frame);
4512
+ document.querySelectorAll(`[data-mpi-check="${options.packageName}"],.mpi-version[data-package="${options.packageName}"]`).forEach((node) => node.remove());
4513
+ };
4514
+ }
4515
+
4226
4516
  // src/client/index.ts
4227
4517
  var name = "dsh-automation-client";
4228
4518
  var inject = ["slots", "locale", "connection", "sessions"];
4519
+ var UPDATE_ICON_PATHS = {
4520
+ refresh: ["M13.5 5.5V2.5m0 0h-3m3 0-2.1 2.1A5.5 5.5 0 1 0 13.2 12"],
4521
+ download: ["M8 2v8m0 0 3-3m-3 3-3-3M3 13v2h10v-2"],
4522
+ copy: ["M5 5h8v8H5z", "M3 3h8"],
4523
+ close: ["m4 4 8 8M12 4 4 12"]
4524
+ };
4525
+ function createPluginUpdateIcon(name2) {
4526
+ const element = document.createElement("span");
4527
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
4528
+ svg.setAttribute("viewBox", "0 0 16 16");
4529
+ svg.setAttribute("width", "16");
4530
+ svg.setAttribute("height", "16");
4531
+ svg.setAttribute("fill", "none");
4532
+ svg.setAttribute("stroke", "currentColor");
4533
+ svg.setAttribute("stroke-width", "1.5");
4534
+ svg.setAttribute("stroke-linecap", "round");
4535
+ svg.setAttribute("stroke-linejoin", "round");
4536
+ svg.setAttribute("aria-hidden", "true");
4537
+ for (const d of UPDATE_ICON_PATHS[name2]) {
4538
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
4539
+ path.setAttribute("d", d);
4540
+ svg.append(path);
4541
+ }
4542
+ element.append(svg);
4543
+ return element;
4544
+ }
4229
4545
  var SETTINGS_CLOCK_SVG = '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" aria-hidden="true" data-dsh-schedule-icon="1"><path fill="currentColor" d="M8 1.15A6.85 6.85 0 1 0 8 14.85 6.85 6.85 0 0 0 8 1.15Zm0 1.4a5.45 5.45 0 1 1 0 10.9 5.45 5.45 0 0 1 0-10.9Z"/><path fill="currentColor" d="M8.62 4.35H7.28v4.2l3.02 1.78.67-1.13-2.35-1.39V4.35Z"/></svg>';
4230
4546
  function isSessionSelector(value) {
4231
4547
  return typeof value === "function";
@@ -4265,6 +4581,15 @@ function installSettingsNavIcon(labels) {
4265
4581
  function apply(ctx) {
4266
4582
  ctx.effect(() => installStyles(), "dsh-automation: styles");
4267
4583
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-automation: locale");
4584
+ ctx.effect(() => observePluginUpdate({
4585
+ endpoint: "/api/michengai/dsh-automation/update",
4586
+ packageName: "@michengai/dsh-automation",
4587
+ titleRowSelector: ".dsh-st-heading-row",
4588
+ linksSelector: ".dsh-st-heading-links",
4589
+ zhName: "\u5B9A\u65F6\u4EFB\u52A1",
4590
+ enName: "Scheduled tasks",
4591
+ createIcon: createPluginUpdateIcon
4592
+ }), "dsh-automation: plugin update ui");
4268
4593
  const t = ctx.locale.bind(NS);
4269
4594
  const permissionT = ctx.locale.bind("permission.access");
4270
4595
  const modelT = ctx.locale.bind("model");