@wenbin_wb/dsh-bridge 2.5.1 → 2.5.3

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/client/client.js CHANGED
@@ -68,6 +68,25 @@ var BRIDGE_ENDPOINTS = {
68
68
  };
69
69
 
70
70
  // client/index.js
71
+ if (typeof window !== "undefined") {
72
+ if (!window.crypto) {
73
+ window.crypto = {};
74
+ }
75
+ if (!window.crypto.randomUUID) {
76
+ window.crypto.randomUUID = function() {
77
+ if (typeof window.crypto.getRandomValues === "function") {
78
+ return ("10000000-1000-4000-8000" + -1e11).replace(/[018]/g, function(c) {
79
+ return (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16);
80
+ });
81
+ }
82
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
83
+ var r = Math.random() * 16 | 0;
84
+ var v = c === "x" ? r : r & 3 | 8;
85
+ return v.toString(16);
86
+ });
87
+ };
88
+ }
89
+ }
71
90
  var GITHUB_URL = "https://github.com/wenbin-wb/dsh-bridge";
72
91
  var RELEASES_URL = "https://github.com/wenbin-wb/dsh-bridge/releases";
73
92
  var ISSUES_URL = "https://github.com/wenbin-wb/dsh-bridge/issues/new";
@@ -1830,14 +1849,59 @@ function BridgePanel({ rpcCall }) {
1830
1849
  const [unlockErr, setUnlockErr] = React.useState(null);
1831
1850
  const [unlocking, setUnlocking] = React.useState(false);
1832
1851
  const [showForgotGuide, setShowForgotGuide] = React.useState(false);
1833
- const authRpcCall = React.useCallback((endpoint, payload = {}, signal) => {
1852
+ const [showUnlockModal, setShowUnlockModal] = React.useState(false);
1853
+ const fetchLoopbackToken = React.useCallback(async () => {
1854
+ if (!isLocalhost) return null;
1855
+ const currentPort = typeof window !== "undefined" ? window.location.port || (window.location.protocol === "https:" ? "443" : "80") : "3082";
1856
+ const proxyPort = status?.proxy?.port || 3082;
1857
+ const candidateUrls = [
1858
+ "/__dsh_bridge__/loopback-token",
1859
+ `http://127.0.0.1:${proxyPort}/__dsh_bridge__/loopback-token`,
1860
+ `http://localhost:${proxyPort}/__dsh_bridge__/loopback-token`,
1861
+ "http://127.0.0.1:3082/__dsh_bridge__/loopback-token"
1862
+ ];
1863
+ const uniqueUrls = [...new Set(candidateUrls)];
1864
+ for (const url of uniqueUrls) {
1865
+ try {
1866
+ const res = await fetch(url, { method: "POST" });
1867
+ if (res.ok) {
1868
+ const data = await res.json();
1869
+ if (data?.ok && data.adminToken) {
1870
+ setAdminToken(data.adminToken);
1871
+ setAdminUnlocked(true);
1872
+ return data.adminToken;
1873
+ }
1874
+ }
1875
+ } catch {
1876
+ }
1877
+ }
1878
+ return null;
1879
+ }, [isLocalhost, status?.proxy?.port]);
1880
+ React.useEffect(() => {
1881
+ if (isLocalhost && !adminUnlocked) {
1882
+ fetchLoopbackToken();
1883
+ }
1884
+ }, [isLocalhost, adminUnlocked, fetchLoopbackToken]);
1885
+ const authRpcCall = React.useCallback(async (endpoint, payload = {}, signal) => {
1886
+ let token = adminToken;
1887
+ if (isLocalhost && !token) {
1888
+ token = await fetchLoopbackToken();
1889
+ }
1834
1890
  const enriched = {
1835
1891
  ...payload,
1836
- ...adminToken ? { adminToken } : {},
1892
+ ...token ? { adminToken: token } : {},
1837
1893
  ...isLocalhost ? { isLocalhost: true } : {}
1838
1894
  };
1839
- return rpcCall(endpoint, enriched, signal);
1840
- }, [rpcCall, adminToken, isLocalhost]);
1895
+ const res = await rpcCall(endpoint, enriched, signal);
1896
+ if (res?.ok === false) {
1897
+ const msg = res?.error?.message || "";
1898
+ if (msg.includes("\u7BA1\u7406\u5458\u6743\u9650") || msg.includes("\u7BA1\u7406\u5BC6\u7801\u89E3\u9501")) {
1899
+ setUnlockErr(msg);
1900
+ setShowUnlockModal(true);
1901
+ }
1902
+ }
1903
+ return res;
1904
+ }, [rpcCall, adminToken, isLocalhost, fetchLoopbackToken]);
1841
1905
  const handleUnlockAdmin = React.useCallback(async (e) => {
1842
1906
  e?.preventDefault?.();
1843
1907
  setUnlocking(true);
@@ -1848,6 +1912,8 @@ function BridgePanel({ rpcCall }) {
1848
1912
  setAdminToken(res.value?.adminToken || "");
1849
1913
  setAdminUnlocked(true);
1850
1914
  setUnlockPassword("");
1915
+ setShowUnlockModal(false);
1916
+ setErr(null);
1851
1917
  } else {
1852
1918
  setUnlockErr(res?.error?.message || "\u7BA1\u7406\u5458\u5BC6\u7801\u9519\u8BEF");
1853
1919
  }
@@ -2204,12 +2270,38 @@ function BridgePanel({ rpcCall }) {
2204
2270
  )
2205
2271
  );
2206
2272
  }
2273
+ const isInterceptionErr = err && (err.includes("\u7BA1\u7406\u5458\u6743\u9650") || err.includes("\u7BA1\u7406\u5BC6\u7801\u89E3\u9501"));
2207
2274
  return React.createElement(
2208
2275
  "div",
2209
- { style: { maxWidth: 620 } },
2210
- err && React.createElement("div", {
2211
- style: { ...s.card, background: "var(--dsw-alias-state-error-bg,#fef2f2)", color: "var(--dsw-alias-state-error-primary,#dc2626)", fontSize: 13, marginBottom: 16 }
2212
- }, err),
2276
+ { style: { maxWidth: 620, position: "relative" } },
2277
+ // 错误横幅(如果是权限拦截,直接提供醒目的输入密码解锁按钮)
2278
+ err && React.createElement(
2279
+ "div",
2280
+ {
2281
+ style: {
2282
+ ...s.card,
2283
+ background: "var(--dsw-alias-state-error-bg,#fef2f2)",
2284
+ color: "var(--dsw-alias-state-error-primary,#dc2626)",
2285
+ fontSize: 13,
2286
+ marginBottom: 16,
2287
+ display: "flex",
2288
+ alignItems: "center",
2289
+ justifyContent: "space-between",
2290
+ flexWrap: "wrap",
2291
+ gap: 10
2292
+ }
2293
+ },
2294
+ React.createElement("span", { style: { flex: "1 1 auto" } }, err),
2295
+ isInterceptionErr && React.createElement("button", {
2296
+ type: "button",
2297
+ style: { ...s.btnPri, background: "#dc2626", color: "#ffffff", height: 26, fontSize: 12, padding: "0 10px", flexShrink: 0 },
2298
+ onClick: () => {
2299
+ setUnlockErr(err);
2300
+ setShowUnlockModal(true);
2301
+ }
2302
+ }, "\u{1F511} \u7ACB\u5373\u8F93\u5165\u7BA1\u7406\u5BC6\u7801\u89E3\u9501")
2303
+ ),
2304
+ // 管理员解锁状态提示条
2213
2305
  !isLocalhost && adminUnlocked && React.createElement(
2214
2306
  "div",
2215
2307
  {
@@ -2232,9 +2324,122 @@ function BridgePanel({ rpcCall }) {
2232
2324
  onClick: handleLockAdmin
2233
2325
  }, "\u{1F512} \u91CD\u65B0\u9501\u5B9A\u540E\u53F0")
2234
2326
  ),
2327
+ // 未解锁时的顶部引导条
2328
+ !isLocalhost && !adminUnlocked && auth?.enabled && policy !== "open" && React.createElement(
2329
+ "div",
2330
+ {
2331
+ style: {
2332
+ display: "flex",
2333
+ alignItems: "center",
2334
+ justifyContent: "space-between",
2335
+ padding: "8px 14px",
2336
+ background: "var(--dsw-alias-state-warn-bg,#fffbeb)",
2337
+ border: "1px solid var(--dsw-alias-state-warn-border,#fde68a)",
2338
+ borderRadius: 8,
2339
+ marginBottom: 14,
2340
+ fontSize: 12,
2341
+ color: "var(--dsw-alias-state-warn-primary,#92400e)"
2342
+ }
2343
+ },
2344
+ React.createElement("span", null, "\u{1F512} \u540E\u53F0\u7BA1\u7406\u6743\u9650\u672A\u89E3\u9501\uFF08\u4FEE\u6539\u654F\u611F\u914D\u7F6E\u9700\u5148\u89E3\u9501\uFF09"),
2345
+ React.createElement("button", {
2346
+ type: "button",
2347
+ style: { ...s.btnPri, height: 24, fontSize: 11, padding: "0 10px", background: "#d97706" },
2348
+ onClick: () => setShowUnlockModal(true)
2349
+ }, "\u{1F511} \u89E3\u9501\u7BA1\u7406\u6743\u9650")
2350
+ ),
2235
2351
  React.createElement(VersionBanner, { rpcCall: authRpcCall }),
2236
2352
  React.createElement(TabBar, { active: activeTab, onChange: setActiveTab, dots }),
2237
- tabContent
2353
+ tabContent,
2354
+ // 全局交互式解锁弹窗 Modal
2355
+ showUnlockModal && React.createElement(
2356
+ "div",
2357
+ {
2358
+ style: {
2359
+ position: "fixed",
2360
+ inset: 0,
2361
+ background: "rgba(0,0,0,0.5)",
2362
+ zIndex: 99999,
2363
+ display: "flex",
2364
+ alignItems: "center",
2365
+ justifyContent: "center",
2366
+ padding: 16
2367
+ },
2368
+ onClick: (e) => {
2369
+ if (e.target === e.currentTarget) setShowUnlockModal(false);
2370
+ }
2371
+ },
2372
+ React.createElement(
2373
+ "div",
2374
+ {
2375
+ style: {
2376
+ background: "var(--dsw-alias-bg-layer-1,#ffffff)",
2377
+ borderRadius: 14,
2378
+ padding: "24px 24px",
2379
+ maxWidth: 420,
2380
+ width: "100%",
2381
+ boxShadow: "0 20px 25px -5px rgba(0,0,0,0.2)",
2382
+ border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)"
2383
+ }
2384
+ },
2385
+ React.createElement(
2386
+ "div",
2387
+ { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 } },
2388
+ React.createElement(
2389
+ "div",
2390
+ { style: { fontSize: 16, fontWeight: 600, color: "var(--dsw-alias-label-primary,currentColor)", display: "flex", alignItems: "center", gap: 8 } },
2391
+ "\u{1F512} \u89E3\u9501\u540E\u53F0\u7BA1\u7406\u6743\u9650"
2392
+ ),
2393
+ React.createElement("button", {
2394
+ type: "button",
2395
+ style: { border: "none", background: "none", cursor: "pointer", fontSize: 18, color: "var(--dsw-alias-label-tertiary,#9ca3af)", padding: 0 },
2396
+ onClick: () => setShowUnlockModal(false)
2397
+ }, "\u2715")
2398
+ ),
2399
+ React.createElement(
2400
+ "div",
2401
+ { style: { fontSize: 13, color: "var(--dsw-alias-label-secondary,#4b5563)", marginBottom: 16, lineHeight: 1.5 } },
2402
+ "\u5F53\u524D\u64CD\u4F5C\u9700\u8981\u540E\u53F0\u7BA1\u7406\u5458\u6743\u9650\u3002\u4E3A\u4FDD\u62A4\u60A8\u7684\u7F51\u7EDC\u914D\u7F6E\u4E0E\u673A\u5668\u4EBA\u5E73\u53F0\u5B89\u5168\uFF0C\u8BF7\u8F93\u5165\u7BA1\u7406\u5BC6\u7801\u89E3\u9501\uFF1A"
2403
+ ),
2404
+ React.createElement(
2405
+ "form",
2406
+ {
2407
+ onSubmit: handleUnlockAdmin,
2408
+ style: { display: "flex", flexDirection: "column", gap: 12 }
2409
+ },
2410
+ React.createElement("input", {
2411
+ type: "password",
2412
+ style: s.input,
2413
+ placeholder: "\u8BF7\u8F93\u5165\u540E\u53F0\u7BA1\u7406\u5BC6\u7801",
2414
+ value: unlockPassword,
2415
+ onChange: (e) => setUnlockPassword(e.target.value),
2416
+ autoFocus: true
2417
+ }),
2418
+ unlockErr && React.createElement("div", {
2419
+ style: { fontSize: 12, color: "var(--dsw-alias-state-error-primary,#dc2626)" }
2420
+ }, unlockErr),
2421
+ React.createElement(
2422
+ "div",
2423
+ { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 } },
2424
+ React.createElement("button", {
2425
+ type: "button",
2426
+ style: s.btnGhost,
2427
+ onClick: () => setShowUnlockModal(false)
2428
+ }, "\u53D6\u6D88"),
2429
+ React.createElement("button", {
2430
+ type: "submit",
2431
+ style: { ...s.btnPri, background: "#4f6ef7", color: "#fff" },
2432
+ disabled: unlocking || !unlockPassword
2433
+ }, unlocking ? "\u9A8C\u8BC1\u4E2D\u2026" : "\u7ACB\u5373\u89E3\u9501")
2434
+ )
2435
+ ),
2436
+ React.createElement(
2437
+ "div",
2438
+ { style: { marginTop: 14, paddingTop: 10, borderTop: "1px solid var(--dsw-alias-border-l2,#f3f4f6)", fontSize: 11, color: "var(--dsw-alias-label-tertiary,#9ca3af)", textAlign: "center", lineHeight: 1.5 } },
2439
+ "\u{1F4A1} \u63D0\u793A\uFF1A\u82E5\u672A\u5355\u72EC\u914D\u7F6E\u7BA1\u7406\u5BC6\u7801\uFF0C\u8BF7\u8F93\u5165\u521D\u6B21\u8BBE\u7F6E\u7684\u8BBF\u95EE\u5BC6\u7801\uFF1B\u7535\u8111\u672C\u673A\uFF08127.0.0.1\uFF09\u8BBF\u95EE\u4EAB\u6709\u514D\u5BC6\u7BA1\u7406\u7279\u6743\u3002"
2440
+ )
2441
+ )
2442
+ )
2238
2443
  );
2239
2444
  }
2240
2445
  function apply(ctx) {
package/client/index.js CHANGED
@@ -1,5 +1,26 @@
1
1
  // dsh-bridge 客户端插件:设置页「远程访问」面板
2
2
 
3
+ // 兼容非 HTTPS 环境(如手机局域网 HTTP 访问):为非安全上下文补齐 crypto.randomUUID
4
+ if (typeof window !== 'undefined') {
5
+ if (!window.crypto) {
6
+ window.crypto = {};
7
+ }
8
+ if (!window.crypto.randomUUID) {
9
+ window.crypto.randomUUID = function() {
10
+ if (typeof window.crypto.getRandomValues === 'function') {
11
+ return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, function(c) {
12
+ return (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> (c / 4)).toString(16);
13
+ });
14
+ }
15
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
16
+ var r = (Math.random() * 16) | 0;
17
+ var v = c === 'x' ? r : (r & 0x3) | 0x8;
18
+ return v.toString(16);
19
+ });
20
+ };
21
+ }
22
+ }
23
+
3
24
  import { BRIDGE_RPC_CHANNEL, BRIDGE_ENDPOINTS } from '../lib/bridge-rpc-constants.js';
4
25
 
5
26
  const GITHUB_URL = 'https://github.com/wenbin-wb/dsh-bridge';
@@ -1624,15 +1645,63 @@ function BridgePanel({ rpcCall }) {
1624
1645
  const [unlockErr, setUnlockErr] = React.useState(null);
1625
1646
  const [unlocking, setUnlocking] = React.useState(false);
1626
1647
  const [showForgotGuide, setShowForgotGuide] = React.useState(false);
1648
+ const [showUnlockModal, setShowUnlockModal] = React.useState(false);
1649
+
1650
+ // 本机物理访问自动静默获取 adminToken,免输密码直通管理(支持 3080 原生端口与 3082 代理端口)
1651
+ const fetchLoopbackToken = React.useCallback(async () => {
1652
+ if (!isLocalhost) return null;
1653
+ const currentPort = typeof window !== 'undefined' ? (window.location.port || (window.location.protocol === 'https:' ? '443' : '80')) : '3082';
1654
+ const proxyPort = status?.proxy?.port || 3082;
1655
+ const candidateUrls = [
1656
+ '/__dsh_bridge__/loopback-token',
1657
+ `http://127.0.0.1:${proxyPort}/__dsh_bridge__/loopback-token`,
1658
+ `http://localhost:${proxyPort}/__dsh_bridge__/loopback-token`,
1659
+ 'http://127.0.0.1:3082/__dsh_bridge__/loopback-token',
1660
+ ];
1661
+ const uniqueUrls = [...new Set(candidateUrls)];
1662
+
1663
+ for (const url of uniqueUrls) {
1664
+ try {
1665
+ const res = await fetch(url, { method: 'POST' });
1666
+ if (res.ok) {
1667
+ const data = await res.json();
1668
+ if (data?.ok && data.adminToken) {
1669
+ setAdminToken(data.adminToken);
1670
+ setAdminUnlocked(true);
1671
+ return data.adminToken;
1672
+ }
1673
+ }
1674
+ } catch {}
1675
+ }
1676
+ return null;
1677
+ }, [isLocalhost, status?.proxy?.port]);
1678
+
1679
+ React.useEffect(() => {
1680
+ if (isLocalhost && !adminUnlocked) {
1681
+ fetchLoopbackToken();
1682
+ }
1683
+ }, [isLocalhost, adminUnlocked, fetchLoopbackToken]);
1627
1684
 
1628
- const authRpcCall = React.useCallback((endpoint, payload = {}, signal) => {
1685
+ const authRpcCall = React.useCallback(async (endpoint, payload = {}, signal) => {
1686
+ let token = adminToken;
1687
+ if (isLocalhost && !token) {
1688
+ token = await fetchLoopbackToken();
1689
+ }
1629
1690
  const enriched = {
1630
1691
  ...payload,
1631
- ...(adminToken ? { adminToken } : {}),
1692
+ ...(token ? { adminToken: token } : {}),
1632
1693
  ...(isLocalhost ? { isLocalhost: true } : {}),
1633
1694
  };
1634
- return rpcCall(endpoint, enriched, signal);
1635
- }, [rpcCall, adminToken, isLocalhost]);
1695
+ const res = await rpcCall(endpoint, enriched, signal);
1696
+ if (res?.ok === false) {
1697
+ const msg = res?.error?.message || '';
1698
+ if (msg.includes('管理员权限') || msg.includes('管理密码解锁')) {
1699
+ setUnlockErr(msg);
1700
+ setShowUnlockModal(true);
1701
+ }
1702
+ }
1703
+ return res;
1704
+ }, [rpcCall, adminToken, isLocalhost, fetchLoopbackToken]);
1636
1705
 
1637
1706
  const handleUnlockAdmin = React.useCallback(async (e) => {
1638
1707
  e?.preventDefault?.();
@@ -1644,6 +1713,8 @@ function BridgePanel({ rpcCall }) {
1644
1713
  setAdminToken(res.value?.adminToken || '');
1645
1714
  setAdminUnlocked(true);
1646
1715
  setUnlockPassword('');
1716
+ setShowUnlockModal(false);
1717
+ setErr(null);
1647
1718
  } else {
1648
1719
  setUnlockErr(res?.error?.message || '管理员密码错误');
1649
1720
  }
@@ -1967,11 +2038,36 @@ function BridgePanel({ rpcCall }) {
1967
2038
  );
1968
2039
  }
1969
2040
 
1970
- return React.createElement('div', { style: { maxWidth: 620 } },
2041
+ const isInterceptionErr = err && (err.includes('管理员权限') || err.includes('管理密码解锁'));
2042
+
2043
+ return React.createElement('div', { style: { maxWidth: 620, position: 'relative' } },
2044
+ // 错误横幅(如果是权限拦截,直接提供醒目的输入密码解锁按钮)
1971
2045
  err && React.createElement('div', {
1972
- style: { ...s.card, background: 'var(--dsw-alias-state-error-bg,#fef2f2)', color: 'var(--dsw-alias-state-error-primary,#dc2626)', fontSize: 13, marginBottom: 16 },
1973
- }, err),
2046
+ style: {
2047
+ ...s.card,
2048
+ background: 'var(--dsw-alias-state-error-bg,#fef2f2)',
2049
+ color: 'var(--dsw-alias-state-error-primary,#dc2626)',
2050
+ fontSize: 13,
2051
+ marginBottom: 16,
2052
+ display: 'flex',
2053
+ alignItems: 'center',
2054
+ justifyContent: 'space-between',
2055
+ flexWrap: 'wrap',
2056
+ gap: 10,
2057
+ },
2058
+ },
2059
+ React.createElement('span', { style: { flex: '1 1 auto' } }, err),
2060
+ isInterceptionErr && React.createElement('button', {
2061
+ type: 'button',
2062
+ style: { ...s.btnPri, background: '#dc2626', color: '#ffffff', height: 26, fontSize: 12, padding: '0 10px', flexShrink: 0 },
2063
+ onClick: () => {
2064
+ setUnlockErr(err);
2065
+ setShowUnlockModal(true);
2066
+ },
2067
+ }, '🔑 立即输入管理密码解锁'),
2068
+ ),
1974
2069
 
2070
+ // 管理员解锁状态提示条
1975
2071
  !isLocalhost && adminUnlocked && React.createElement('div', {
1976
2072
  style: {
1977
2073
  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
@@ -1987,11 +2083,90 @@ function BridgePanel({ rpcCall }) {
1987
2083
  }, '🔒 重新锁定后台'),
1988
2084
  ),
1989
2085
 
2086
+ // 未解锁时的顶部引导条
2087
+ !isLocalhost && !adminUnlocked && auth?.enabled && policy !== 'open' && React.createElement('div', {
2088
+ style: {
2089
+ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
2090
+ padding: '8px 14px', background: 'var(--dsw-alias-state-warn-bg,#fffbeb)',
2091
+ border: '1px solid var(--dsw-alias-state-warn-border,#fde68a)', borderRadius: 8,
2092
+ marginBottom: 14, fontSize: 12, color: 'var(--dsw-alias-state-warn-primary,#92400e)',
2093
+ },
2094
+ },
2095
+ React.createElement('span', null, '🔒 后台管理权限未解锁(修改敏感配置需先解锁)'),
2096
+ React.createElement('button', {
2097
+ type: 'button',
2098
+ style: { ...s.btnPri, height: 24, fontSize: 11, padding: '0 10px', background: '#d97706' },
2099
+ onClick: () => setShowUnlockModal(true),
2100
+ }, '🔑 解锁管理权限'),
2101
+ ),
2102
+
1990
2103
  React.createElement(VersionBanner, { rpcCall: authRpcCall }),
1991
2104
 
1992
2105
  React.createElement(TabBar, { active: activeTab, onChange: setActiveTab, dots }),
1993
2106
 
1994
2107
  tabContent,
2108
+
2109
+ // 全局交互式解锁弹窗 Modal
2110
+ showUnlockModal && React.createElement('div', {
2111
+ style: {
2112
+ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 99999,
2113
+ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16,
2114
+ },
2115
+ onClick: (e) => { if (e.target === e.currentTarget) setShowUnlockModal(false); },
2116
+ },
2117
+ React.createElement('div', {
2118
+ style: {
2119
+ background: 'var(--dsw-alias-bg-layer-1,#ffffff)', borderRadius: 14,
2120
+ padding: '24px 24px', maxWidth: 420, width: '100%',
2121
+ boxShadow: '0 20px 25px -5px rgba(0,0,0,0.2)', border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)',
2122
+ },
2123
+ },
2124
+ React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 } },
2125
+ React.createElement('div', { style: { fontSize: 16, fontWeight: 600, color: 'var(--dsw-alias-label-primary,currentColor)', display: 'flex', alignItems: 'center', gap: 8 } },
2126
+ '🔒 解锁后台管理权限'
2127
+ ),
2128
+ React.createElement('button', {
2129
+ type: 'button',
2130
+ style: { border: 'none', background: 'none', cursor: 'pointer', fontSize: 18, color: 'var(--dsw-alias-label-tertiary,#9ca3af)', padding: 0 },
2131
+ onClick: () => setShowUnlockModal(false),
2132
+ }, '✕'),
2133
+ ),
2134
+ React.createElement('div', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary,#4b5563)', marginBottom: 16, lineHeight: 1.5 } },
2135
+ '当前操作需要后台管理员权限。为保护您的网络配置与机器人平台安全,请输入管理密码解锁:'
2136
+ ),
2137
+ React.createElement('form', {
2138
+ onSubmit: handleUnlockAdmin,
2139
+ style: { display: 'flex', flexDirection: 'column', gap: 12 },
2140
+ },
2141
+ React.createElement('input', {
2142
+ type: 'password',
2143
+ style: s.input,
2144
+ placeholder: '请输入后台管理密码',
2145
+ value: unlockPassword,
2146
+ onChange: (e) => setUnlockPassword(e.target.value),
2147
+ autoFocus: true,
2148
+ }),
2149
+ unlockErr && React.createElement('div', {
2150
+ style: { fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#dc2626)' },
2151
+ }, unlockErr),
2152
+ React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 } },
2153
+ React.createElement('button', {
2154
+ type: 'button',
2155
+ style: s.btnGhost,
2156
+ onClick: () => setShowUnlockModal(false),
2157
+ }, '取消'),
2158
+ React.createElement('button', {
2159
+ type: 'submit',
2160
+ style: { ...s.btnPri, background: '#4f6ef7', color: '#fff' },
2161
+ disabled: unlocking || !unlockPassword,
2162
+ }, unlocking ? '验证中…' : '立即解锁'),
2163
+ ),
2164
+ ),
2165
+ React.createElement('div', { style: { marginTop: 14, paddingTop: 10, borderTop: '1px solid var(--dsw-alias-border-l2,#f3f4f6)', fontSize: 11, color: 'var(--dsw-alias-label-tertiary,#9ca3af)', textAlign: 'center', lineHeight: 1.5 } },
2166
+ '💡 提示:若未单独配置管理密码,请输入初次设置的访问密码;电脑本机(127.0.0.1)访问享有免密管理特权。'
2167
+ ),
2168
+ ),
2169
+ ),
1995
2170
  );
1996
2171
  }
1997
2172
 
package/docs/banner.jpg CHANGED
Binary file
@@ -69,6 +69,8 @@
69
69
  3. 点击 **「保存并连接」**;
70
70
  4. 状态显示为绿色 **「已连接」** 即表示长连接成功建立!
71
71
 
72
+ ![飞书机器人配置](screenshots/feishu-bot-config.jpg)
73
+
72
74
  ---
73
75
 
74
76
  ## 💬 常用操作与指令
@@ -91,3 +93,6 @@
91
93
  当 Agent 尝试执行需授权的操作(如终端命令、写敏感文件)时,飞书端会自动推送 **原生交互卡片**:
92
94
  - 点击卡片上的 **「✓ 批准执行」** 或 **「✕ 拒绝执行」** 按钮即可一键处理;
93
95
  - 亦可直接回复文字 `/yes` (或 `1`) / `/no` (或 `2`)。
96
+
97
+ ![飞书对话与卡片审批](screenshots/feishu-chat.jpg)
98
+
Binary file
Binary file
@@ -49,6 +49,8 @@
49
49
  - 亦可直接在系统环境变量中设置 `HTTPS_PROXY=http://127.0.0.1:7890`;
50
50
  5. 点击「**保存并连接**」。
51
51
 
52
+ ![Telegram Bot 配置](screenshots/telegram-bot-config.jpg)
53
+
52
54
  ---
53
55
 
54
56
  ## 📱 第三步:扫码与自动白名单授权
@@ -5,7 +5,8 @@
5
5
  import fs from 'node:fs'
6
6
  import path from 'node:path'
7
7
  import { Service } from '@deepseek-ai/cordis'
8
- import * as Lark from '@larksuiteoapi/node-sdk'
8
+ import LarkSdk from './lark-bundled.mjs'
9
+ const Lark = LarkSdk.default || LarkSdk
9
10
 
10
11
  export class FeishuGateway extends Service {
11
12
  static name = 'feishu'