@linxin666/dsh-remote-web-ui 0.1.15 → 0.1.17

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/lib/index.js CHANGED
@@ -981,6 +981,10 @@ function makeRoutes(deps) {
981
981
  function mobileBundlePath() {
982
982
  return fileURLToPath(new URL("../lib/mobile.js", import.meta.url));
983
983
  }
984
+ /** The apple-touch-icon shipped with the package (iOS home-screen icon, 180x180 PNG). */
985
+ function appleTouchIconPath() {
986
+ return fileURLToPath(new URL("../assets/apple-touch-icon.png", import.meta.url));
987
+ }
984
988
  /** The mobile page shell: minimal, offline-safe, no external assets. */
985
989
  function pageHtml(bundleUrl) {
986
990
  return [
@@ -991,6 +995,7 @@ function pageHtml(bundleUrl) {
991
995
  "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover\">",
992
996
  "<meta name=\"theme-color\" content=\"#f3f5f9\">",
993
997
  "<meta name=\"referrer\" content=\"no-referrer\">",
998
+ "<link rel=\"apple-touch-icon\" href=\"/m/apple-touch-icon.png\">",
994
999
  "<title>移动端远程控制</title>",
995
1000
  "</head>",
996
1001
  "<body>",
@@ -1009,6 +1014,15 @@ function writeStatic(res, status, type, body) {
1009
1014
  });
1010
1015
  res.end(body);
1011
1016
  }
1017
+ /** Send the PNG icon as a binary body (unlike text assets, the PNG must not be decoded to utf-8). */
1018
+ function writePng(res, status, body) {
1019
+ res.writeHead(status, {
1020
+ "content-type": "image/png",
1021
+ "cache-control": "public, max-age=31536000, immutable",
1022
+ "referrer-policy": "no-referrer"
1023
+ });
1024
+ res.end(body);
1025
+ }
1012
1026
  /**
1013
1027
  * Build the mobile page routes.
1014
1028
  * @returns the two exact routes to register on webServer.
@@ -1029,15 +1043,35 @@ function makeMobileRoutes() {
1029
1043
  writeStatic(res, 500, "text/plain", "failed to read the mobile bundle");
1030
1044
  }
1031
1045
  };
1032
- return [{
1033
- kind: "exact",
1034
- path: "/m",
1035
- handler: handlePage
1036
- }, {
1037
- kind: "exact",
1038
- path: "/m/mobile.js",
1039
- handler: handleBundle
1040
- }];
1046
+ const handleIcon = async (_req, res) => {
1047
+ const path = appleTouchIconPath();
1048
+ if (!existsSync(path)) {
1049
+ writeStatic(res, 404, "text/plain", "apple-touch-icon not found");
1050
+ return;
1051
+ }
1052
+ try {
1053
+ writePng(res, 200, await readFile(path));
1054
+ } catch {
1055
+ writeStatic(res, 500, "text/plain", "failed to read the apple-touch-icon");
1056
+ }
1057
+ };
1058
+ return [
1059
+ {
1060
+ kind: "exact",
1061
+ path: "/m",
1062
+ handler: handlePage
1063
+ },
1064
+ {
1065
+ kind: "exact",
1066
+ path: "/m/mobile.js",
1067
+ handler: handleBundle
1068
+ },
1069
+ {
1070
+ kind: "exact",
1071
+ path: "/m/apple-touch-icon.png",
1072
+ handler: handleIcon
1073
+ }
1074
+ ];
1041
1075
  }
1042
1076
  //#endregion
1043
1077
  //#region src/mobile-api.ts
@@ -1096,10 +1130,22 @@ const MOBILE_API_METHOD_PREFIX = `${MOBILE_API_PREFIX}/`;
1096
1130
  function makeMobileApiRoutes(deps) {
1097
1131
  const { service, apiProxy, mobileEnterToSend } = deps;
1098
1132
  const eventsHeartbeatMs = deps.eventsHeartbeatMs ?? DEFAULT_EVENTS_HEARTBEAT_MS;
1133
+ /**
1134
+ * Refresh the paired device's presence and report whether it is live.
1135
+ * The mobile surface (unlike the desktop Web UI) has no `/api/pair/heartbeat`
1136
+ * sender, so any activity on the mobile channel — a gated RPC, or the live
1137
+ * SSE stream staying open — must count as presence. Without this, an
1138
+ * idle-but-connected phone ages past `offlineAfterMs` and the desktop panel
1139
+ * wrongly reports it as disconnected.
1140
+ */
1141
+ const touchDeviceFor = (req) => {
1142
+ const deviceId = readCookie(req.headers.cookie, service.config.cookieName);
1143
+ if (deviceId === void 0) return false;
1144
+ return service.touchDevice(deviceId);
1145
+ };
1099
1146
  /** The phone gate: a live paired-device cookie, or nothing else proceeds. */
1100
1147
  const gateOk = (req) => {
1101
- const deviceId = readCookie(req.headers.cookie, service.config.cookieName);
1102
- return deviceId !== void 0 && service.hasDevice(deviceId);
1148
+ return touchDeviceFor(req);
1103
1149
  };
1104
1150
  const writeJson = (res, status, body) => {
1105
1151
  res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
@@ -1219,6 +1265,7 @@ function makeMobileApiRoutes(deps) {
1219
1265
  let closed = false;
1220
1266
  const heartbeat = setInterval(() => {
1221
1267
  if (closed) return;
1268
+ touchDeviceFor(req);
1222
1269
  try {
1223
1270
  res.write(": ping\n\n");
1224
1271
  } catch {}
@@ -1633,13 +1680,16 @@ function isLinkedSpec(spec) {
1633
1680
  }
1634
1681
  /**
1635
1682
  * Resolve the anchor package's manifest path. The aggregate package is the
1636
- * canonical entry point; this plugin's own package is the fallback.
1683
+ * canonical entry point; this plugin's own package is the fallback. Both a
1684
+ * throwing resolve and an undefined return mean "not installed" and move on
1685
+ * to the next candidate.
1637
1686
  * @param resolve - a Node resolve implementation scoped to the host process.
1638
1687
  * @returns the absolute manifest path, or undefined when neither is installed.
1639
1688
  */
1640
1689
  function resolveAnchorManifest(resolve) {
1641
1690
  for (const name of [AGGREGATE_PACKAGE, SELF_PACKAGE]) try {
1642
- return resolve(name + "/package.json");
1691
+ const path = resolve(name + "/package.json");
1692
+ if (path !== void 0) return path;
1643
1693
  } catch {}
1644
1694
  }
1645
1695
  /**
@@ -1701,14 +1751,21 @@ async function fetchLatestVersion(name, fetchImpl, timeoutMs = 1e4) {
1701
1751
  return;
1702
1752
  }
1703
1753
  }
1754
+ /**
1755
+ * Sentinel for an installed version that could not be read (missing manifest
1756
+ * or resolve failure). It is a real-looking version so `checkUpdates` can
1757
+ * render the affected row, but the verified-update comparison must never
1758
+ * treat it as evidence — a read failure is not a version that "moved".
1759
+ */
1760
+ const VERSION_UNKNOWN = "0.0.0";
1704
1761
  /** The resolved current version of one family package (probe failure tolerated). */
1705
1762
  function readInstalledVersion(resolve, name) {
1706
1763
  try {
1707
1764
  const path = resolve(name + "/package.json");
1708
1765
  const version = path === void 0 ? void 0 : readManifest(path)?.version;
1709
- return typeof version === "string" ? version : "0.0.0";
1766
+ return typeof version === "string" ? version : VERSION_UNKNOWN;
1710
1767
  } catch {
1711
- return "0.0.0";
1768
+ return VERSION_UNKNOWN;
1712
1769
  }
1713
1770
  }
1714
1771
  /**
@@ -1777,6 +1834,16 @@ const OUTPUT_CAP = 16 * 1024;
1777
1834
  */
1778
1835
  const WIN_CMD_MISSING_RE = /not recognized as an internal or external command/i;
1779
1836
  /**
1837
+ * Bypass for pnpm 11's supply-chain gate: `minimumReleaseAge` (default 24 h)
1838
+ * silently skips same-day releases — `pnpm update --latest` then exits 0
1839
+ * without moving anything and the post-run verification would misreport a
1840
+ * stale no-op. The update here is explicitly user-initiated and the panel
1841
+ * shows exactly which versions are being installed, so the gate only adds a
1842
+ * confusing silent no-op; override it per-invocation instead of asking every
1843
+ * user to edit the profile's pnpm-workspace.yaml.
1844
+ */
1845
+ const MIN_RELEASE_AGE_OVERRIDE = "--config.minimumReleaseAge=0";
1846
+ /**
1780
1847
  * Run the update inside the profile directory. Tries pnpm first, falls back
1781
1848
  * to corepack and then npx when the previous command is missing (ENOENT);
1782
1849
  * all candidates share one hard timeout and keep accumulating output.
@@ -1800,13 +1867,20 @@ function runUpdate(deps) {
1800
1867
  const candidates = [
1801
1868
  {
1802
1869
  command: "pnpm",
1803
- args: ["update", ...packages]
1870
+ args: [
1871
+ "update",
1872
+ "--latest",
1873
+ MIN_RELEASE_AGE_OVERRIDE,
1874
+ ...packages
1875
+ ]
1804
1876
  },
1805
1877
  {
1806
1878
  command: "corepack",
1807
1879
  args: [
1808
1880
  "pnpm",
1809
1881
  "update",
1882
+ "--latest",
1883
+ MIN_RELEASE_AGE_OVERRIDE,
1810
1884
  ...packages
1811
1885
  ]
1812
1886
  },
@@ -1816,6 +1890,8 @@ function runUpdate(deps) {
1816
1890
  "--yes",
1817
1891
  "pnpm",
1818
1892
  "update",
1893
+ "--latest",
1894
+ MIN_RELEASE_AGE_OVERRIDE,
1819
1895
  ...packages
1820
1896
  ]
1821
1897
  }
@@ -1912,6 +1988,72 @@ function runUpdate(deps) {
1912
1988
  runCandidate(0);
1913
1989
  });
1914
1990
  }
1991
+ /**
1992
+ * Run the update, then verify the installed versions actually moved. pnpm
1993
+ * exits 0 even when it silently kept the installed versions — runUpdate
1994
+ * overrides pnpm 11's `minimumReleaseAge` gate (default 24 h) with
1995
+ * `--config.minimumReleaseAge=0`, but an older pnpm without the override or
1996
+ * another silent no-op can still keep versions in place — so a green exit
1997
+ * alone must not report a misleading "update complete". Re-read the
1998
+ * installed versions afterwards and surface a `stale` failure (with the
1999
+ * captured pnpm output) when nothing moved, so the panel can tell the user
2000
+ * how to unblock the gate instead of claiming success.
2001
+ *
2002
+ * The stale decision anchors on the pre-run installed versions, not on the
2003
+ * registry latest: under a lenient gate pnpm may move 0.1.12 -> 0.1.13 while
2004
+ * latest stays 0.1.15 — that is a real update, not stale. The post-run anchor
2005
+ * path is re-resolved rather than reused from boot time: pnpm removes the old
2006
+ * version's .pnpm directory on update, so a boot-time captured path would
2007
+ * fail to read and collapse a successful update into a bogus 'missing'.
2008
+ *
2009
+ * A green exit whose verification has nothing to compare (registry probe
2010
+ * outage, missing anchor, non-npm mode) is reported as `verify-failed` — it
2011
+ * must never read as success. A green exit where no package moved but the
2012
+ * post-run check could not prove the install is fully current (a partial
2013
+ * probe failure hides whether a gate kept something back) is also
2014
+ * `verify-failed`: it must not collapse into a success either.
2015
+ * @param deps - the run and check seams.
2016
+ * @returns the run result; `stale` when exit 0 left every version in place,
2017
+ * `verify-failed` when the post-run check could not verify anything.
2018
+ */
2019
+ async function runUpdateVerified(deps) {
2020
+ const before = /* @__PURE__ */ new Map();
2021
+ for (const name of deps.run.packages) {
2022
+ const version = readInstalledVersion(deps.check.resolve, name);
2023
+ if (version !== VERSION_UNKNOWN) before.set(name, version);
2024
+ }
2025
+ const result = await runUpdate(deps.run);
2026
+ if (!result.ok) return result;
2027
+ const anchorManifestPath = resolveAnchorManifest(deps.check.resolve) ?? deps.check.anchorManifestPath;
2028
+ const status = await checkUpdates({
2029
+ ...deps.check,
2030
+ anchorManifestPath
2031
+ });
2032
+ if (status.error !== void 0 || status.mode !== "npm") return {
2033
+ ...result,
2034
+ ok: false,
2035
+ errorCode: "verify-failed",
2036
+ error: "pnpm exited 0 but the post-run version check could not verify the install"
2037
+ };
2038
+ if (status.packages.some((packageStatus) => {
2039
+ const beforeVersion = before.get(packageStatus.name);
2040
+ if (beforeVersion === void 0 || packageStatus.current === VERSION_UNKNOWN) return false;
2041
+ return packageStatus.current !== beforeVersion;
2042
+ })) return result;
2043
+ if (status.outdated) return {
2044
+ ...result,
2045
+ ok: false,
2046
+ errorCode: "stale",
2047
+ error: "pnpm exited 0 but the installed versions did not change"
2048
+ };
2049
+ if (status.packages.some((packageStatus) => packageStatus.latest === void 0)) return {
2050
+ ...result,
2051
+ ok: false,
2052
+ errorCode: "verify-failed",
2053
+ error: "pnpm exited 0 but the post-run version check could not verify the install"
2054
+ };
2055
+ return result;
2056
+ }
1915
2057
  //#endregion
1916
2058
  //#region src/update-routes.ts
1917
2059
  /** Route paths (exact matches under /api). */
@@ -2093,11 +2235,17 @@ function apply(ctx, config) {
2093
2235
  const apiProxy = ctx.get("apiProxy");
2094
2236
  if (apiProxy === void 0) console.warn("remote-web-ui: apiProxy service unavailable — the mobile data channel is disabled");
2095
2237
  const requireFromHost = createRequire(import.meta.url);
2096
- const anchorManifestPath = resolveAnchorManifest((specifier) => requireFromHost.resolve(specifier));
2238
+ const resolveAnchorPath = () => resolveAnchorManifest((specifier) => {
2239
+ try {
2240
+ return requireFromHost.resolve(specifier);
2241
+ } catch {
2242
+ return;
2243
+ }
2244
+ });
2097
2245
  const updateRoutes = makeUpdateRoutes({
2098
2246
  fence: (request) => isTrustedApiRequest(request, []),
2099
2247
  check: () => checkUpdates({
2100
- anchorManifestPath,
2248
+ anchorManifestPath: resolveAnchorPath(),
2101
2249
  resolve: (specifier) => {
2102
2250
  try {
2103
2251
  return requireFromHost.resolve(specifier);
@@ -2108,7 +2256,7 @@ function apply(ctx, config) {
2108
2256
  fetchLatest: (name) => fetchLatestVersion(name, fetch)
2109
2257
  }),
2110
2258
  run: async () => {
2111
- const target = resolveUpdateTarget({ anchorManifestPath });
2259
+ const target = resolveUpdateTarget({ anchorManifestPath: resolveAnchorPath() });
2112
2260
  if ("error" in target) {
2113
2261
  const code = target.error;
2114
2262
  return {
@@ -2119,9 +2267,22 @@ function apply(ctx, config) {
2119
2267
  errorCode: code
2120
2268
  };
2121
2269
  }
2122
- return runUpdate({
2123
- profileDir: target.profileDir,
2124
- packages: target.packages
2270
+ return runUpdateVerified({
2271
+ run: {
2272
+ profileDir: target.profileDir,
2273
+ packages: target.packages
2274
+ },
2275
+ check: {
2276
+ anchorManifestPath: resolveAnchorPath(),
2277
+ resolve: (specifier) => {
2278
+ try {
2279
+ return requireFromHost.resolve(specifier);
2280
+ } catch {
2281
+ return;
2282
+ }
2283
+ },
2284
+ fetchLatest: (name) => fetchLatestVersion(name, fetch)
2285
+ }
2125
2286
  });
2126
2287
  }
2127
2288
  });
@@ -1 +1 @@
1
- {"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,oGAAoG;AACpG,eAAO,MAAM,cAAc,kMAUjB,CAAA;AAEV,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD,oEAAoE;AACpE,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,WAAW,CAAA;AAE9E,wDAAwD;AACxD,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAA;IACnF,uCAAuC;IACvC,QAAQ,EAAE,IAAI,CAAA;IACd,4EAA4E;IAC5E,cAAc,EAAE,IAAI,CAAA;IACpB,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,CAAC,sCA2GpG;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAA;IACV,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,UAAU,EAAE,OAAO,CAAA;IACnB,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IACjB,wBAAwB;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,oEAAoE;IACpE,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,+BAqCA;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG;IAC/C,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,+BAmCA;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG;IAC9C,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,OAAO,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzD,+BAsCA"}
1
+ {"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,oGAAoG;AACpG,eAAO,MAAM,cAAc,kMAUjB,CAAA;AAEV,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD,oEAAoE;AACpE,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,WAAW,CAAA;AAE9E,wDAAwD;AACxD,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAA;IACnF,uCAAuC;IACvC,QAAQ,EAAE,IAAI,CAAA;IACd,4EAA4E;IAC5E,cAAc,EAAE,IAAI,CAAA;IACpB,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,CAAC,sCAiHpG;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAA;IACV,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,UAAU,EAAE,OAAO,CAAA;IACnB,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IACjB,wBAAwB;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,oEAAoE;IACpE,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,+BAqCA;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG;IAC/C,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,+BAmCA;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG;IAC9C,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,OAAO,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzD,+BAsCA"}
@@ -46,7 +46,9 @@ export function PluginSettingsCard(props) {
46
46
  : null] }));
47
47
  }
48
48
  return (_jsxs("li", { className: cardClass, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null, _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }), open
49
- ? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: props.t('settings.readOnly') }) : null, props.children, _jsxs("div", { className: css.footer, children: [state.failed ? _jsx("p", { className: css.failed, role: "status", children: props.t('settings.saveFailed') }) : null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.onDiscard, children: props.t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.onSave, children: props.t(!state.saving ? 'settings.save' : 'settings.saving') })] })] }))
49
+ ? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: props.t('settings.readOnly') }) : null, props.children, _jsxs("div", { className: css.footer, children: [state.failed
50
+ ? (_jsxs("p", { className: css.failed, role: "status", children: [props.t('settings.saveFailed'), state.failedReason ? ' - ' + state.failedReason : ''] }))
51
+ : null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.onDiscard, children: props.t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.onSave, children: props.t(!state.saving ? 'settings.save' : 'settings.saving') })] })] }))
50
52
  : null] }));
51
53
  }
52
54
  /** A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec. */
@@ -77,6 +77,8 @@ function errorMessageOf(t, result) {
77
77
  case "not-found": return t("update.error.notFound");
78
78
  case "link": return t("update.error.link");
79
79
  case "pnpm-failed": return t("update.error.pnpmFailed", { code: String(result.exitCode ?? "?") });
80
+ case "stale": return t("update.error.stale");
81
+ case "verify-failed": return t("update.error.verifyFailed");
80
82
  default: return result.error ?? t("update.error.unknown");
81
83
  }
82
84
  }
@@ -99,6 +99,8 @@ export declare const zh: {
99
99
  'update.error.notFound': string;
100
100
  'update.error.link': string;
101
101
  'update.error.pnpmFailed': string;
102
+ 'update.error.stale': string;
103
+ 'update.error.verifyFailed': string;
102
104
  'update.error.unknown': string;
103
105
  };
104
106
  /** The remote namespace key union. */
@@ -205,6 +207,8 @@ export declare const en: {
205
207
  'update.error.notFound': string;
206
208
  'update.error.link': string;
207
209
  'update.error.pnpmFailed': string;
210
+ 'update.error.stale': string;
211
+ 'update.error.verifyFailed': string;
208
212
  'update.error.unknown': string;
209
213
  };
210
214
  //# sourceMappingURL=locales.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGmB,CAAA;AAElC,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AAEvC,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAA;AAEvC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGsB,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGmB,CAAA;AAElC,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AAEvC,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAA;AAEvC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGsB,CAAA"}
@@ -101,6 +101,8 @@ export const zh = {
101
101
  'update.error.notFound': '未找到 dsh-web-ui 聚合包安装,无法更新。',
102
102
  'update.error.link': '当前为本地开发模式(链接安装),无法自动更新。',
103
103
  'update.error.pnpmFailed': '更新执行失败(pnpm 退出码 {code}),详见下方输出。',
104
+ 'update.error.stale': '更新命令已执行,但安装版本未变化。常见原因:pnpm 11 的 minimumReleaseAge 门禁会静默跳过发布不足 24 小时的新版本。请在 profile 目录(~/.dsh/profiles/<profile>)的 pnpm-workspace.yaml 中配置 minimumReleaseAgeExclude(例如 "@linxin666/*")或 minimumReleaseAge: 0 后重新点击更新。',
105
+ 'update.error.verifyFailed': '更新命令已执行,但无法核对安装后的版本(可能是 registry 探测失败或安装路径变化)。请查看下方输出,或稍后重试。',
104
106
  'update.error.unknown': '更新失败,请重试。',
105
107
  };
106
108
  /** English dictionary, checked complete against the zh key set. */
@@ -205,5 +207,7 @@ export const en = {
205
207
  'update.error.notFound': 'The dsh-web-ui aggregate package is not installed; nothing to update.',
206
208
  'update.error.link': 'Local development mode (link install) — automatic update is unavailable.',
207
209
  'update.error.pnpmFailed': 'The update failed (pnpm exited with code {code}); see the output below.',
210
+ 'update.error.stale': 'The update command ran but the installed versions did not change. Likely cause: the pnpm 11 minimumReleaseAge gate silently skips releases published less than 24 hours ago. Add minimumReleaseAgeExclude (e.g. "@linxin666/*") or set minimumReleaseAge: 0 in pnpm-workspace.yaml under the profile directory (~/.dsh/profiles/<profile>), then run the update again.',
211
+ 'update.error.verifyFailed': 'The update command ran but the installed versions could not be verified afterwards (registry probe failed or the install path changed). Inspect the output below, or retry later.',
208
212
  'update.error.unknown': 'Update failed; try again.',
209
213
  };
@@ -18,6 +18,14 @@ export type FieldWrite = {
18
18
  export interface FieldSpec {
19
19
  /** Field name inside the namespace section. */
20
20
  field: string;
21
+ /**
22
+ * Whether the Host treats this field as a secret and redacts its value from
23
+ * the read-back (role('secret') in the section schema). Redacted secrets are
24
+ * never compared against the draft on save; the field lands when the scope
25
+ * reports the write succeeded (its secret-set marker under the bridge), so
26
+ * a successful secret save is not misreported as failed.
27
+ */
28
+ secret?: boolean;
21
29
  /** Render a stored value as draft text; the empty string when the section carries none. */
22
30
  format: (value: unknown) => string;
23
31
  /**
@@ -57,6 +65,12 @@ export interface CardShell {
57
65
  saving: boolean;
58
66
  /** Whether the last save did not land as staged; cleared by the next edit or save. */
59
67
  failed: boolean;
68
+ /**
69
+ * The rejection code/message the Host returned for the last failed save,
70
+ * surfaced next to the generic failure text. Undefined while no save has
71
+ * failed (or the failure carried no server reason).
72
+ */
73
+ failedReason?: string;
60
74
  }
61
75
  /** The write actions the card's slot entry injects. */
62
76
  export interface CardActions {
@@ -69,6 +83,37 @@ export interface CardActions {
69
83
  /** Drop every staged edit. */
70
84
  discard: () => void;
71
85
  }
86
+ /** One durable write a batched settings scope performs. */
87
+ export interface BatchedWrite {
88
+ /** Field this entry writes. */
89
+ field: string;
90
+ /** set stores a value; unset drops the leaf. */
91
+ op: 'set' | 'unset';
92
+ /** Value for op set (absent for unset). */
93
+ value?: unknown;
94
+ }
95
+ /** Per-field outcome of one batched scope write. */
96
+ export interface BatchedFieldResult {
97
+ /** Field this entry writes. */
98
+ field: string;
99
+ /** Whether the Host accepted this field's write (per the read-back view). */
100
+ landed: boolean;
101
+ }
102
+ /**
103
+ * Result of a batched scope write. The bridge scope posts every planned write
104
+ * in one /mutate so the Host validate hook judges baseURL+model together; a
105
+ * batched refusal fails the whole save rather than per-field.
106
+ */
107
+ export interface BatchResult {
108
+ /** Whether the whole mutate was accepted. */
109
+ ok: boolean;
110
+ /** Per-field success, in the request order (always present when ok). */
111
+ fields: BatchedFieldResult[];
112
+ /** Host rejection code (mutate refused). */
113
+ code?: string;
114
+ /** Host rejection message (mutate refused). */
115
+ message?: string;
116
+ }
72
117
  /** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */
73
118
  export interface NumberConstraints {
74
119
  /** The accepted value must be a whole number. */
@@ -80,6 +125,13 @@ export interface NumberConstraints {
80
125
  export declare function numberField(field: string, constraints?: NumberConstraints): FieldSpec;
81
126
  /** A free-text field. An empty draft clears the field. */
82
127
  export declare function textField(field: string): FieldSpec;
128
+ /**
129
+ * A free-text field the Host treats as a secret and redacts from the read-back
130
+ * (role('secret') in the section schema). The card still edits it like text,
131
+ * but a save never compares the redacted value back and relies on the scope
132
+ * reporting the write landed.
133
+ */
134
+ export declare function secretField(field: string): FieldSpec;
83
135
  /** A boolean field, edited through true/false draft text. */
84
136
  export declare function booleanField(field: string): FieldSpec;
85
137
  /** An enumerated string field; only the listed choices are accepted. An empty draft clears the field. */
@@ -99,6 +151,7 @@ export declare class CardForm<T> {
99
151
  private readonly listeners;
100
152
  private saving;
101
153
  private failed;
154
+ private failedReason;
102
155
  /** @param scope - the bound settings scope for this card's namespace. */
103
156
  constructor(scope: SettingsScope<T>, specs: FieldSpec[]);
104
157
  /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */
@@ -111,9 +164,18 @@ export declare class CardForm<T> {
111
164
  actions(): CardActions;
112
165
  /**
113
166
  * Write every staged edit, then re-seed from what the Host accepted.
167
+ *
168
+ * When the scope carries the optional batch surface (the dsh-web-ui
169
+ * bridge scope), every planned write rides one mutation so cross-field
170
+ * validate hooks (baseURL+model) judge the batch as a unit instead of
171
+ * deadlocking on per-field writes. Otherwise the per-field loop runs.
172
+ * A field lands only when the Host reports it held the staged value; a
173
+ * landed field's draft is dropped, a failed one stays staged for the user.
114
174
  * @returns settlement after every write and the read-back.
115
175
  */
116
176
  save(): Promise<void>;
177
+ /** The scope's batch surface when it supports one; undefined conservatively otherwise. */
178
+ private batchedScope;
117
179
  /**
118
180
  * Every staged edit a save would write. An entry whose draft is not a value
119
181
  * its field accepts carries no write: the form is still dirty, and the save
@@ -1 +1 @@
1
- {"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}