@artenx/dshb 0.1.31 → 0.1.33

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.
Files changed (36) hide show
  1. package/lib/client.js +133 -22
  2. package/lib/client.js.map +1 -1
  3. package/lib/core/index.js +6 -1
  4. package/lib/core/index.js.map +1 -1
  5. package/lib/core/sync/coordinator.d.ts +2 -2
  6. package/lib/core/sync/coordinator.js +23 -6
  7. package/lib/core/sync/coordinator.js.map +1 -1
  8. package/lib/core/sync/internal-routes.d.ts +1 -1
  9. package/lib/core/sync/internal-routes.js +80 -4
  10. package/lib/core/sync/internal-routes.js.map +1 -1
  11. package/lib/core/sync/offline-archive.d.ts +1 -1
  12. package/lib/core/sync/offline-archive.js +1 -1
  13. package/lib/core/sync/offline-routes.js +1 -1
  14. package/lib/core/sync/protocol.d.ts +2 -2
  15. package/lib/core/sync/protocol.js +4 -1
  16. package/lib/core/sync/protocol.js.map +1 -1
  17. package/lib/core/sync/remote-helper.js +107 -76
  18. package/lib/core/sync/remote-helper.js.map +1 -1
  19. package/lib/core/sync/routes.d.ts +1 -1
  20. package/lib/core/sync/routes.js +61 -1
  21. package/lib/core/sync/routes.js.map +1 -1
  22. package/lib/core/sync/session-routes.d.ts +1 -1
  23. package/lib/core/sync/session-routes.js +1 -1
  24. package/lib/core/sync/session-sync.d.ts +2 -2
  25. package/lib/core/sync/session-sync.js +2 -2
  26. package/lib/core/sync/ssh-transport.d.ts +2 -2
  27. package/lib/core/sync/ssh-transport.js +267 -3
  28. package/lib/core/sync/ssh-transport.js.map +1 -1
  29. package/lib/{protocol-KaWZArY8.d.ts → protocol-DJ0zyVlB.d.ts} +5 -2
  30. package/lib/{session-sync-Zfo1Q-qT.d.ts → session-sync-BuOJvFxK.d.ts} +54 -2
  31. package/lib/{session-sync-BQt3HXkp.js → session-sync-CIqjuWkh.js} +455 -48
  32. package/lib/session-sync-CIqjuWkh.js.map +1 -0
  33. package/lib/ssh-transport-C7povM0s.d.ts +115 -0
  34. package/package.json +1 -1
  35. package/lib/session-sync-BQt3HXkp.js.map +0 -1
  36. package/lib/ssh-transport-Cfb_nwkY.d.ts +0 -65
package/lib/client.js CHANGED
@@ -2264,6 +2264,34 @@ window.__ModuleLoader__.load({
2264
2264
  secrets
2265
2265
  };
2266
2266
  }
2267
+ /**
2268
+ * 把探测结果回填进表单:DSH_HOME 取首个命中目录(保留用户已填的显式覆盖),
2269
+ * profile 取首个装有 dshb 的条目,Web 端口取握手命中值。仅在对应字段为空
2270
+ * 或明显是默认值时回填,避免覆盖用户手改内容。
2271
+ */
2272
+ function applyRemoteProbeToForm(form, probe) {
2273
+ const detectedHome = probe.homes[0];
2274
+ const detectedProfile = probe.profiles.find((profile) => profile.dshbInstalled) ?? probe.profiles[0];
2275
+ const homeKept = form.remoteDshHome.trim() !== "" && form.remoteDshHome.trim() !== (detectedHome ?? "");
2276
+ const profileKept = form.remoteProfile.trim() !== "" && form.remoteProfile.trim() !== "web";
2277
+ return {
2278
+ ...form,
2279
+ remoteDshHome: homeKept ? form.remoteDshHome : detectedHome ?? form.remoteDshHome,
2280
+ remoteProfile: profileKept ? form.remoteProfile : detectedProfile?.name ?? form.remoteProfile,
2281
+ remoteWebPort: probe.webPort !== void 0 ? String(probe.webPort) : form.remoteWebPort
2282
+ };
2283
+ }
2284
+ /** 远端 profile 下拉选项:探测结果优先(dshb 已装的排前),并保留当前手填值。 */
2285
+ function remoteProfileOptions(form, probe) {
2286
+ const options = (probe?.profiles ?? []).slice().sort((left, right) => Number(right.dshbInstalled) - Number(left.dshbInstalled)).map((profile) => ({
2287
+ name: profile.name,
2288
+ dshbInstalled: profile.dshbInstalled
2289
+ }));
2290
+ const current = form.remoteProfile.trim();
2291
+ if (current && !options.some((option) => option.name === current)) options.push({ name: current });
2292
+ if (options.length === 0) options.push({ name: "web" });
2293
+ return options;
2294
+ }
2267
2295
  //#endregion
2268
2296
  //#region src/client/sync-peers.tsx
2269
2297
  function shortenSessionId(id) {
@@ -2364,6 +2392,8 @@ window.__ModuleLoader__.load({
2364
2392
  const [form, setForm] = (0, react.useState)(EMPTY_SYNC_PEER_FORM);
2365
2393
  const [busy, setBusy] = (0, react.useState)(false);
2366
2394
  const [probing, setProbing] = (0, react.useState)(false);
2395
+ const [probingRemote, setProbingRemote] = (0, react.useState)(false);
2396
+ const [remoteProbe, setRemoteProbe] = (0, react.useState)(void 0);
2367
2397
  const [syncing, setSyncing] = (0, react.useState)(false);
2368
2398
  const [notice, setNotice] = (0, react.useState)(void 0);
2369
2399
  const [hostKeyChallenge, setHostKeyChallenge] = (0, react.useState)(void 0);
@@ -2482,6 +2512,48 @@ window.__ModuleLoader__.load({
2482
2512
  setProbing(false);
2483
2513
  }
2484
2514
  };
2515
+ const probeRemote = async () => {
2516
+ const secretReady = form.authKind === "password" ? form.password !== "" : form.privateKey !== "";
2517
+ if (selectedId === "new" && (!form.host.trim() || !form.username.trim() || !secretReady)) {
2518
+ setNotice({
2519
+ kind: "error",
2520
+ text: "探测远端前请先填写 SSH 主机、系统账号和登录凭据"
2521
+ });
2522
+ return;
2523
+ }
2524
+ setProbingRemote(true);
2525
+ setHostKeyChallenge(void 0);
2526
+ try {
2527
+ const useSavedCredentials = selectedId !== "new" && !secretReady;
2528
+ const request = buildSyncPeerFormRequest(form);
2529
+ const response = await fetch("/api/dshb/sync/peers/probe-remote", {
2530
+ method: "POST",
2531
+ headers: { "content-type": "application/json" },
2532
+ body: JSON.stringify(useSavedCredentials ? { peerId: selectedId } : {
2533
+ ...request.fields,
2534
+ secrets: request.secrets
2535
+ })
2536
+ });
2537
+ const data = await readJsonResponse(response, "远端探测失败");
2538
+ if (!response.ok || !data.ok || !data.probe) throw new Error(data.error ?? "远端探测失败");
2539
+ const probe = data.probe;
2540
+ setRemoteProbe(probe);
2541
+ setForm((current) => applyRemoteProbeToForm(current, probe));
2542
+ const profileText = probe.profiles.length > 0 ? `${probe.profiles.length} 个 profile` : "未发现 profile";
2543
+ const portText = probe.webPort !== void 0 ? `Web 端口 ${probe.webPort}` : "未探测到运行中的 Web 端口";
2544
+ setNotice({
2545
+ kind: "ok",
2546
+ text: `远端探测完成:${profileText},${portText}${probe.instanceId ? `(实例 ${probe.instanceId.slice(0, 14)}…)` : ""}`
2547
+ });
2548
+ } catch (error) {
2549
+ setNotice({
2550
+ kind: "error",
2551
+ text: error instanceof Error ? error.message : "远端探测失败"
2552
+ });
2553
+ } finally {
2554
+ setProbingRemote(false);
2555
+ }
2556
+ };
2485
2557
  const trustObservedHostKey = async () => {
2486
2558
  if (selectedId === "new" || !hostKeyChallenge) return;
2487
2559
  const confirmation = hostKeyChallenge.code === "host-key-changed" ? `远端 SSH 主机指纹已变化。\n当前保存:${hostKeyChallenge.expected}\n本次观察:${hostKeyChallenge.fingerprint}\n确认信任新指纹?` : `首次连接观察到 SSH 主机指纹:\n${hostKeyChallenge.fingerprint}\n请核对后确认信任。`;
@@ -2886,24 +2958,41 @@ window.__ModuleLoader__.load({
2886
2958
  }))
2887
2959
  })]
2888
2960
  })] }),
2889
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2961
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2890
2962
  style: {
2891
2963
  ...groupTitle,
2892
- marginTop: 20
2964
+ marginTop: 20,
2965
+ display: "flex",
2966
+ alignItems: "center",
2967
+ justifyContent: "space-between"
2893
2968
  },
2894
- children: "远端配置"
2969
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "远端配置" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2970
+ type: "button",
2971
+ style: probingRemote ? secondaryBtn : secondaryBtn,
2972
+ disabled: probingRemote,
2973
+ onClick: () => {
2974
+ probeRemote();
2975
+ },
2976
+ children: probingRemote ? "探测中…" : "通过 SSH 探测远端"
2977
+ })]
2895
2978
  }),
2896
2979
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2897
2980
  style: field,
2898
2981
  children: [
2899
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2900
2983
  style: label,
2901
- children: "远端 DSH_HOME"
2984
+ children: ["远端 DSH_HOME", remoteProbe?.homes.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2985
+ style: {
2986
+ fontWeight: 400,
2987
+ opacity: .55
2988
+ },
2989
+ children: "(已探测)"
2990
+ }) : void 0]
2902
2991
  }),
2903
2992
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2904
2993
  style: inputStyle$1,
2905
2994
  value: form.remoteDshHome,
2906
- placeholder: "默认使用 SSH 账号的 ~/.dsh",
2995
+ placeholder: "点击上方按钮通过 SSH 自动探测",
2907
2996
  onChange: (e) => setForm((c) => ({
2908
2997
  ...c,
2909
2998
  remoteDshHome: e.target.value
@@ -2911,7 +3000,7 @@ window.__ModuleLoader__.load({
2911
3000
  }),
2912
3001
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2913
3002
  style: hint,
2914
- children: "远端 DSH 数据目录,留空使用默认路径"
3003
+ children: remoteProbe?.homes.length ? `探测到 ${remoteProbe.homes.length} 个候选目录,已回填首个命中` : "留空时由探测自动发现;也可手动填写远端 DSH 数据目录"
2915
3004
  })
2916
3005
  ]
2917
3006
  }),
@@ -2919,24 +3008,46 @@ window.__ModuleLoader__.load({
2919
3008
  className: "dshb-sync-two-col",
2920
3009
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2921
3010
  style: field,
2922
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2923
- style: label,
2924
- children: "远端 profile"
2925
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2926
- style: inputStyle$1,
2927
- value: form.remoteProfile,
2928
- placeholder: "web",
2929
- onChange: (e) => setForm((c) => ({
2930
- ...c,
2931
- remoteProfile: e.target.value
2932
- }))
2933
- })]
3011
+ children: [
3012
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3013
+ style: label,
3014
+ children: ["远端 profile", remoteProbe?.profiles.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3015
+ style: {
3016
+ fontWeight: 400,
3017
+ opacity: .55
3018
+ },
3019
+ children: "(已探测)"
3020
+ }) : void 0]
3021
+ }),
3022
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
3023
+ style: inputStyle$1,
3024
+ value: form.remoteProfile,
3025
+ onChange: (e) => setForm((c) => ({
3026
+ ...c,
3027
+ remoteProfile: e.target.value
3028
+ })),
3029
+ children: remoteProfileOptions(form, remoteProbe).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
3030
+ value: option.name,
3031
+ children: [option.name, option.dshbInstalled === true ? "(DSHB 已安装)" : option.dshbInstalled === false ? "(未安装 DSHB)" : ""]
3032
+ }, option.name))
3033
+ }),
3034
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3035
+ style: hint,
3036
+ children: "由探测列出;未探测时默认 web"
3037
+ })
3038
+ ]
2934
3039
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2935
3040
  style: field,
2936
3041
  children: [
2937
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3042
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2938
3043
  style: label,
2939
- children: "远端 Web 端口"
3044
+ children: ["远端 Web 端口", remoteProbe?.webPort !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3045
+ style: {
3046
+ fontWeight: 400,
3047
+ opacity: .55
3048
+ },
3049
+ children: "(已探测)"
3050
+ }) : void 0]
2940
3051
  }),
2941
3052
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2942
3053
  style: inputStyle$1,
@@ -2950,7 +3061,7 @@ window.__ModuleLoader__.load({
2950
3061
  }),
2951
3062
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2952
3063
  style: hint,
2953
- children: "留空使用默认 3080"
3064
+ children: remoteProbe?.webPort !== void 0 ? `已回填 ${remoteProbe.webPort}${remoteProbe.instanceId ? `(实例 ${remoteProbe.instanceId.slice(0, 14)}…)` : ""}${(remoteProbe.handshakes?.length ?? 0) > 1 ? `;同机检测到多个实例:${remoteProbe.handshakes?.map((hit) => hit.port).join("、") ?? ""}` : ""}` : "留空使用默认 3080;桌面版远端也走本机回环,无需公网暴露"
2954
3065
  })
2955
3066
  ]
2956
3067
  })]