@dshfly/remote-connector 0.2.1 → 0.3.0
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/core/connector-core.js +70 -2
- package/core/deps-check-cli.js +33 -0
- package/core/deps-check.js +103 -0
- package/core/dsh-web.js +15 -1
- package/core/history-slim.js +59 -0
- package/core/tunnel.js +42 -13
- package/core/version.js +63 -0
- package/dist/client.js +31 -2
- package/dist/client.js.map +2 -2
- package/http-api.js +75 -3
- package/index.js +5 -1
- package/mobile-bridge/core/bridge-core.js +7 -5
- package/mobile-bridge/core/files.js +22 -1
- package/mobile-bridge/core/git-status.js +121 -0
- package/mobile-bridge/index.js +1 -0
- package/package.json +5 -4
package/dist/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.jsx", "../src/client/locales.js", "../src/client/SettingsSection.jsx", "../src/client/api.js"],
|
|
4
|
-
"sourcesContent": ["// packages/remote-connector/src/client/index.jsx\n// @dshfly/remote-connector \u7684 DSH web client \u5165\u53E3\uFF08\u6D4F\u89C8\u5668\u4FA7\uFF09\u3002\n// \u5951\u7EA6\uFF08\u5BF9\u9F50 orbis @orbisapp/remote-dsh/src/client/index.tsx\uFF09\uFF1A\n// exports[\"./client\"] \u6307\u5411\u6784\u5EFA\u4EA7\u7269\uFF1Bdsh.client.inject \u58F0\u660E\u4F9D\u8D56\uFF1B\n// apply(ClientContext) \u6CE8\u518C\u8BCD\u5178 + \u5411\u8BBE\u7F6E\u7A97\u53E3\u6CE8\u518C\u72EC\u7ACB section\uFF08\u51FA\u73B0\u5728\u8BBE\u7F6E\u5DE6\u4FA7\u83DC\u5355\uFF09\u3002\n\nimport { zh, en } from './locales';\nimport { SettingsSection } from './SettingsSection';\n\nconst namespace = 'dshfly';\n\nexport const inject = ['slots', 'locale'];\n\nexport function apply(context) {\n context.effect(\n () => context.locale.register(namespace, { zh, en }),\n 'dshfly-remote-connector: dictionaries',\n );\n const translate = context.locale.bind(namespace);\n const injected = () => ({ t: translate });\n // settings.section\uFF08list slot\uFF09\uFF1A\u8BBE\u7F6E\u7A97\u53E3\u5DE6\u4FA7\u83DC\u5355\u7684\u6570\u636E\u6E90\u2014\u2014\u6BCF\u4E2A entry \u4E00\u4E2A\u83DC\u5355\u9879\uFF0C\n // \u70B9\u51FB\u540E\u53F3\u4FA7\u5185\u5BB9\u533A\u6E32\u67D3\u5BF9\u5E94\u7EC4\u4EF6\uFF08props: { close } + inject \u7684 { t }\uFF09\u3002\n // \u81EA settings.plugins.tab\uFF08\"\u8BBE\u7F6E-\u63D2\u4EF6\" tab \u5185\uFF09\u8FC1\u5165\uFF082026-08\uFF09\uFF1A\u914D\u5BF9\u9762\u677F\u6210\u4E3A\u72EC\u7ACB\u83DC\u5355\u9879\u3002\n context.slots.inject('settings.section', () =>\n context.slots.register(\n {\n name: 'settings.section',\n id: 'dshfly-remote-connector',\n order: 30, // \u4F4D\u4E8E\u5185\u7F6E models(10) \u7B49\u4E4B\u540E\n label: () => translate('nav'),\n inject: injected,\n },\n SettingsSection,\n ),\n );\n}\n", "// packages/remote-connector/src/client/locales.js\n// \u8BBE\u7F6E\u9875\u8BCD\u5178\uFF08zh/en\uFF0Clocale.register \u53CC\u8BED\u6587\u6863\u8981\u6C42\u5E73\u8861\uFF09\u3002\n\nexport const zh = {\n nav: '\u8FDC\u7A0B\u8FDE\u63A5',\n statusConnected: '\u5DF2\u8FDE\u63A5',\n statusConnecting: '\u8FDE\u63A5\u4E2D',\n statusDisconnected: '\u672A\u8FDE\u63A5',\n relayServer: '\u4E2D\u7EE7\u670D\u52A1\u5668',\n version: '\u7248\u672C',\n checkUpdate: '\u68C0\u67E5\u66F4\u65B0',\n updateAvailable: '\u53D1\u73B0\u65B0\u7248\u672C',\n upgrade: '\u4E00\u952E\u5347\u7EA7',\n upgrading: '\u5347\u7EA7\u4E2D\u2026',\n upgradeDone: '\u5347\u7EA7\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F dsh web \u751F\u6548',\n upgradeFailed: '\u5347\u7EA7\u5931\u8D25',\n upToDate: '\u5DF2\u662F\u6700\u65B0\u7248\u672C',\n checkFailed: '\u68C0\u67E5\u66F4\u65B0\u5931\u8D25',\n accountName: '\u8D26\u53F7',\n deviceId: '\u8BBE\u5907 ID',\n hostname: '\u8BA1\u7B97\u673A\u540D',\n pairedDevices: '\u5DF2\u914D\u5BF9\u8BBE\u5907',\n noDevices: '\u6682\u65E0\u5DF2\u914D\u5BF9\u8BBE\u5907',\n lastUsed: '\u6700\u8FD1\u4F7F\u7528',\n never: '\u4ECE\u672A',\n unpair: '\u89E3\u9664\u914D\u5BF9',\n deviceOnline: '\u5728\u7EBF',\n deviceOffline: '\u79BB\u7EBF',\n startPairing: '\u65B0\u5EFA\u914D\u5BF9',\n pairingTitle: '\u914D\u5BF9\u4E8C\u7EF4\u7801',\n waitingScan: '\u7B49\u5F85\u624B\u673A\u626B\u7801\u2026\uFF0810 \u5206\u949F\u6709\u6548\uFF09',\n recommendedTitle: '\u63A8\u8350\u63D2\u4EF6',\n recommendedHint: '\u88C5\u5230\u672C\u673A dsh web \u540E\uFF0C\u624B\u673A App \u7684\u63D2\u4EF6 Tab \u4F1A\u81EA\u52A8\u51FA\u73B0\uFF1A',\n install: '\u4E00\u952E\u5B89\u88C5',\n installing: '\u5B89\u88C5\u4E2D\u2026',\n installed: '\u5DF2\u5B89\u88C5',\n installDone: '\u5B89\u88C5\u5B8C\u6210\uFF0C\u65E0\u9700\u91CD\u542F',\n installReboot: '\u5DF2\u5B89\u88C5\uFF0C\u9700\u91CD\u542F dsh web \u540E\u751F\u6548',\n installFailed: '\u5B89\u88C5\u5931\u8D25',\n awaitingConfirm: '\u6709\u4E00\u53F0\u624B\u673A\u8BF7\u6C42\u4E0E\u672C\u673A\u914D\u5BF9',\n confirmPairing: '\u914D\u5BF9\u8BF7\u6C42',\n approve: '\u786E\u8BA4\u901A\u8FC7',\n reject: '\u62D2\u7EDD',\n pairingDone: '\u914D\u5BF9\u5B8C\u6210',\n pairingRejected: '\u5DF2\u62D2\u7EDD\u8BE5\u914D\u5BF9\u8BF7\u6C42',\n copyLink: '\u590D\u5236\u94FE\u63A5',\n copied: '\u5DF2\u590D\u5236',\n refresh: '\u5237\u65B0',\n operationFailed: '\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5',\n loadFailed: '\u52A0\u8F7D\u72B6\u6001\u5931\u8D25',\n confirmUnpair: '\u786E\u8BA4\u89E3\u9664\u8FD9\u53F0\u8BBE\u5907\u7684\u914D\u5BF9\uFF1F',\n fullDisk: '\u5141\u8BB8\u6D4F\u89C8\u5B8C\u6574\u78C1\u76D8',\n fullDiskHint: '\u5F00\u542F\u540E\u624B\u673A\u53EF\u6D4F\u89C8\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u5E76\u65B0\u5EFA\u4EFB\u610F\u4F4D\u7F6E\u7684\u5DE5\u4F5C\u533A\uFF1B\u5173\u95ED\u5219\u4EC5\u9650\u5DF2\u767B\u8BB0\u5DE5\u4F5C\u533A\u4E0E dsh web \u4E3B\u76EE\u5F55',\n permissionChanged: '\u6743\u9650\u5DF2\u66F4\u65B0',\n intro: '\u901A\u8FC7\u624B\u673A App \u968F\u65F6\u968F\u5730\u8FDE\u63A5 DeepSeek Harness\u2014\u2014\u7AEF\u5230\u7AEF\u52A0\u5BC6\u3001\u5B89\u5168\u4E2D\u7EE7\uFF0C\u590D\u6742\u7F51\u7EDC\u73AF\u5883\u4E5F\u80FD\u7A33\u5B9A\u8FDE\u63A5\u3002',\n confirm: '\u786E\u5B9A',\n cancel: '\u53D6\u6D88',\n scanHint: '\u5728\u624B\u673A\u4E0A\u4F7F\u7528 DSH Fly \u626B\u7801',\n shortCodeHint: '\u8BF7\u6838\u5BF9\u624B\u673A\u5C4F\u5E55\u4E0A\u7684\u914D\u5BF9\u7801',\n shortCodeCompare: '\u4E0E\u624B\u673A\u300C\u914D\u5BF9\u5B8C\u6210\u300D\u9875\u663E\u793A\u7684 8 \u4F4D\u7801\u4E00\u81F4\u624D\u53EF\u786E\u8BA4\u2014\u2014\u7801\u4E0D\u540C\u8BF4\u660E\u4E2D\u7EE7\u88AB\u7BE1\u6539',\n knownDevice: '\u8FD9\u662F\u5DF2\u914D\u5BF9\u8FC7\u7684\u8BBE\u5907\uFF0C\u786E\u8BA4\u540E\u514D\u7801\u91CD\u8FDE',\n tokenRevoked: '\u8BBE\u5907\u5DF2\u64A4\u9500',\n tokenRevokedTip: '\u672C\u673A\u5DF2\u88AB\u4E2D\u7EE7\u64A4\u9500\uFF0C\u9700\u91CD\u65B0\u521D\u59CB\u5316\u540E\u518D\u914D\u5BF9',\n tokenExpired: '\u51ED\u8BC1\u5DF2\u8FC7\u671F',\n tokenExpiredTip: '\u8BBE\u5907\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u81EA\u52A8\u5237\u65B0\uFF08\u4E00\u822C\u51E0\u79D2\u5185\u6062\u590D\uFF09',\n tokenExpiring: '\u51ED\u8BC1\u5373\u5C06\u8FC7\u671F',\n tokenExpiringTip: '\u8BBE\u5907\u51ED\u8BC1\u5C06\u5728 12 \u5C0F\u65F6\u5185\u8FC7\u671F\uFF0C\u4F1A\u81EA\u52A8\u5237\u65B0',\n tokenUnregistered: '\u672A\u767B\u8BB0',\n tokenUnregisteredTip: '\u672C\u673A\u5C1A\u672A\u5728\u4E2D\u7EE7\u767B\u8BB0\uFF0C\u65B0\u5EFA\u914D\u5BF9\u4F1A\u81EA\u52A8\u767B\u8BB0',\n};\n\nexport const en = {\n nav: 'Remote Connector',\n statusConnected: 'Connected',\n statusConnecting: 'Connecting',\n statusDisconnected: 'Disconnected',\n relayServer: 'Relay server',\n version: 'Version',\n checkUpdate: 'Check update',\n updateAvailable: 'New version available',\n upgrade: 'Upgrade',\n upgrading: 'Upgrading\u2026',\n upgradeDone: 'Upgrade done, restart dsh web to apply',\n upgradeFailed: 'Upgrade failed',\n upToDate: 'Up to date',\n checkFailed: 'Update check failed',\n accountName: 'Account',\n deviceId: 'Device ID',\n hostname: 'Hostname',\n pairedDevices: 'Paired devices',\n noDevices: 'No paired devices',\n lastUsed: 'Last used',\n never: 'never',\n unpair: 'Unpair',\n deviceOnline: 'Online',\n deviceOffline: 'Offline',\n startPairing: 'New pairing',\n pairingTitle: 'Pairing QR',\n waitingScan: 'Waiting for phone scan\u2026 (10 min)',\n awaitingConfirm: 'A phone is requesting to pair with this machine',\n confirmPairing: 'Pairing request',\n approve: 'Approve',\n reject: 'Reject',\n pairingDone: 'Paired',\n pairingRejected: 'Pairing request rejected',\n copyLink: 'Copy link',\n copied: 'Copied',\n refresh: 'Refresh',\n operationFailed: 'Operation failed, retry',\n loadFailed: 'Failed to load status',\n confirmUnpair: 'Unpair this device?',\n fullDisk: 'Allow full disk browsing',\n fullDiskHint: 'When on, the phone can browse the whole filesystem and create workspaces anywhere; when off, it is limited to registered workspaces and the dsh web home directory',\n permissionChanged: 'Permission updated',\n intro: 'Connect to DeepSeek Harness from your phone anytime, anywhere \u2014 end-to-end encrypted, secure relay, stable under any network conditions.',\n confirm: 'OK',\n cancel: 'Cancel',\n scanHint: 'Scan with the DSH Fly app on your phone',\n shortCodeHint: 'Compare with the pairing code on the phone screen',\n shortCodeCompare: 'Only confirm if it matches the 8-char code on the phone\\'s \"paired\" screen \u2014 a mismatch means the relay was tampered with',\n knownDevice: 'This device was paired before; confirm to reconnect without a code',\n tokenRevoked: 'Device revoked',\n tokenRevokedTip: 'This machine was revoked by the relay; reinitialize before pairing',\n tokenExpired: 'Credential expired',\n tokenExpiredTip: 'Device credential expired, auto-refreshing (usually recovers in seconds)',\n tokenExpiring: 'Credential expiring',\n tokenExpiringTip: 'Device credential expires within 12h, auto-refreshed',\n tokenUnregistered: 'Unregistered',\n tokenUnregisteredTip: 'Not registered on the relay; a new pairing registers it automatically',\n recommendedTitle: 'Recommended plugins',\n recommendedHint: 'Installed into this dsh web, they appear in the app plugin tab automatically:',\n install: 'Install',\n installing: 'Installing\u2026',\n installed: 'Installed',\n installDone: 'Installed, no restart needed',\n installReboot: 'Installed, restart dsh web to apply',\n installFailed: 'Install failed',\n};\n", "// packages/remote-connector/src/client/SettingsSection.jsx\n// dsh web \u8BBE\u7F6E\u9875\"\u63D2\u4EF6\"tab \u7684\u914D\u5BF9\u9762\u677F\uFF1A\u8FDE\u63A5\u72B6\u6001 / \u914D\u5BF9\u4E8C\u7EF4\u7801 / \u8BBE\u5907\u7BA1\u7406 / \u4E8C\u6B21\u786E\u8BA4\u3002\n// \u96F6\u914D\u7F6E\uFF1ArelayUrl/\u8D26\u53F7\u5747\u6765\u81EA\u63D2\u4EF6\u540E\u7AEF\uFF08/dshfly/status\uFF09\uFF0C\u672C\u9762\u677F\u53EA\u505A\u5C55\u793A\u4E0E\u64CD\u4F5C\u3002\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { getStatus, startPairing, getPairing, confirmPairing, rejectPairing, unpair, setPermission, getRecommendedPlugins, installPlugin, checkUpdate, upgrade } from './api';\n\nconst sectionStyle = {\n display: 'grid',\n gap: 20,\n maxWidth: 760, // \u5BF9\u9F50 DSH \u5185\u7F6E\u8BBE\u7F6E\u9875 section\uFF08.pbvGtq_section max-width:760px\uFF09\n padding: '8px 0 32px',\n};\n\nconst cardStyle = {\n border: '1px solid var(--dsh-border, rgba(127, 127, 127, .28))',\n borderRadius: 12,\n padding: 16,\n display: 'grid',\n gap: 12,\n};\n\nconst rowStyle = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n flexWrap: 'wrap',\n};\n\nconst labelStyle = { fontSize: 12, opacity: 0.65 };\n\nconst badgeStyle = (color) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n fontSize: 13,\n padding: '3px 10px',\n borderRadius: 999,\n border: '1px solid currentColor',\n color,\n});\n\nconst btnStyle = {\n border: '1px solid var(--dsh-border, rgba(127, 127, 127, .35))',\n borderRadius: 8,\n background: 'transparent',\n color: 'inherit',\n padding: '8px 14px',\n cursor: 'pointer',\n fontSize: 13,\n};\n\n// \u6BCF\u53F0\u914D\u5BF9\u8BBE\u5907\u7684\u8FDE\u63A5\u72B6\u6001\u63D0\u793A\uFF08M5.12 \u65B9\u6848C\uFF1A\u624B\u673A /events \u5728\u7EBF\u72B6\u6001\uFF0C\u8BBE\u5907\u5217\u8868\"\u89E3\u9664\u914D\u5BF9\"\u6309\u94AE\u5DE6\u4FA7\uFF09\nconst deviceStatusStyle = (online) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 5,\n fontSize: 12,\n padding: '2px 8px',\n borderRadius: 999,\n border: '1px solid currentColor',\n color: online ? '#2f9e44' : '#868e96',\n whiteSpace: 'nowrap',\n});\n\n// \u6743\u9650\u5F00\u5173\uFF08\u786E\u8BA4\u5F39\u7A97 / \u8BBE\u5907\u5217\u8868\u5171\u7528\uFF09\uFF1Acheckbox \u6837\u5F0F\nconst switchStyle = { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 };\n\n// \u914D\u5BF9\u786E\u8BA4\u5F39\u7A97\uFF08\u5C45\u4E2D\u906E\u7F69\uFF0C\u786E\u8BA4/\u62D2\u7EDD\u6309\u94AE\u9192\u76EE\uFF09\nconst modalBackdropStyle = {\n position: 'fixed', inset: 0, zIndex: 9999,\n background: 'rgba(0, 0, 0, 0.55)',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24,\n};\nconst modalCardStyle = {\n background: 'var(--dsh-card-bg, #fff)',\n color: 'var(--dsh-card-fg, #1a1d26)',\n borderRadius: 16, padding: 24, maxWidth: 380, width: '100%',\n boxShadow: '0 12px 40px rgba(0,0,0,.35)',\n};\nconst modalTitleStyle = { fontSize: 17, fontWeight: '700', marginBottom: 10 };\n\nfunction statusBadge(connection, t, relayUrl) {\n // title\uFF1A\u60AC\u6D6E\u63D0\u793A\u4E2D\u7EE7\u670D\u52A1\u5668\u5730\u5740\uFF08\u4E0D\u76F4\u63A5\u5C55\u793A\uFF0C\u9632\u7BE1\u6539\u66B4\u9732\u9762\uFF1B2026-08 \u5B89\u5168\u6536\u7D27\uFF09\n const tip = relayUrl ? `${t('relayServer')}: ${relayUrl}` : undefined;\n if (connection === 'connected') return <span style={badgeStyle('#2f9e44')} title={tip}>\u25CF {t('statusConnected')}</span>;\n if (connection === 'connecting') return <span style={badgeStyle('#e8890c')} title={tip}>\u25D0 {t('statusConnecting')}</span>;\n return <span style={badgeStyle('#c92a2a')} title={tip}>\u25CB {t('statusDisconnected')}</span>;\n}\n\n// device token \u5065\u5EB7\u72B6\u6001\uFF082026-08 \u81EA\u6108\uFF09\uFF1A\u63D0\u524D\u9884\u8B66\"\u5373\u5C06\u8FC7\u671F/\u5DF2\u8FC7\u671F/\u5DF2\u64A4\u9500\"\uFF0C\u800C\u975E 401 \u624D\u53BB\u731C\nfunction tokenBadge(tokenState, t) {\n // expiringAt \u53EA\u505A\u60AC\u6D6E\u63D0\u793A\uFF08\u771F\u5B9E\u72B6\u6001\u81EA\u6108\u540E\u81EA\u52A8\u7FFB\u8F6C\uFF0C\u4E0D\u9700\u5C55\u793A\u5177\u4F53\u65F6\u95F4\uFF09\n if (tokenState === 'revoked') return <span style={badgeStyle('#c92a2a')} title={t('tokenRevokedTip')}>\u26A0 {t('tokenRevoked')}</span>;\n if (tokenState === 'expired') return <span style={badgeStyle('#c92a2a')} title={t('tokenExpiredTip')}>\u26A0 {t('tokenExpired')}</span>;\n if (tokenState === 'expiring') return <span style={badgeStyle('#e8890c')} title={t('tokenExpiringTip')}>\u25D0 {t('tokenExpiring')}</span>;\n if (tokenState === 'unregistered') return <span style={badgeStyle('#868e96')} title={t('tokenUnregisteredTip')}>\u25CB {t('tokenUnregistered')}</span>;\n // 'ok' / 'unknown' / \u7F3A\u7701\uFF1A\u4E0D\u8BEF\u5BFC\n return null;\n}\n\nfunction fmtTime(ts, t) {\n if (!ts) return t('never');\n const d = new Date(ts);\n return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;\n}\n\nexport function SettingsSection({ t }) {\n const [status, setStatus] = useState(null);\n const [pairing, setPairing] = useState(null); // {pairingId, fp, deepLink, qrDataUrl, phase, phonePk?}\n const [confirmFullDisk, setConfirmFullDisk] = useState(false); // \u914D\u5BF9\u786E\u8BA4\u65F6\u7684\u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF08\u9ED8\u8BA4\u5173\uFF09\n const [busy, setBusy] = useState(null);\n const [error, setError] = useState(null);\n const [notice, setNotice] = useState(null);\n const [recommended, setRecommended] = useState(null); // \u63A8\u8350\u63D2\u4EF6\u5217\u8868\uFF08null = \u672A\u52A0\u8F7D\uFF09\n const [installBusy, setInstallBusy] = useState(null); // \u6B63\u5728\u5B89\u88C5\u7684\u63D2\u4EF6 id\n const [installNotice, setInstallNotice] = useState(null); // {id, text, kind}\n const [updateInfo, setUpdateInfo] = useState(null); // {current, latest, updateAvailable, error?} \u5347\u7EA7\u68C0\u67E5\n const [updateBusy, setUpdateBusy] = useState(false);\n const [toast, setToast] = useState(null); // {text, kind} \u5F31\u5F39\u7A97\uFF08\u81EA\u52A8\u6D88\u5931\uFF09\n\n // \u5F31\u5F39\u7A97\uFF1A\u663E\u793A\u540E 3s \u81EA\u52A8\u6D88\u5931\uFF08\u68C0\u67E5\u5931\u8D25/\u5347\u7EA7\u7ED3\u679C\u7528\uFF0C\u4E0D\u5E38\u9A7B\u754C\u9762\uFF09\n const toastTimer = useRef(null);\n const showToast = useCallback((text, kind = 'ok') => {\n setToast({ text, kind });\n if (toastTimer.current) clearTimeout(toastTimer.current);\n toastTimer.current = setTimeout(() => setToast(null), 3000);\n }, []);\n\n // \u5347\u7EA7\u68C0\u67E5\uFF1Amanual=false\uFF08\u8FDB\u5165\u9875\u9762\u81EA\u52A8\uFF09\u5931\u8D25\u9759\u9ED8\uFF1Bmanual=true\uFF08\u7528\u6237\u70B9\u51FB\uFF09\u5931\u8D25/\u5DF2\u6700\u65B0\u8D70\u5F31\u5F39\u7A97\n const loadUpdate = useCallback(async (manual = false) => {\n try {\n const r = await checkUpdate();\n setUpdateInfo(r);\n if (manual) {\n if (r.updateAvailable) showToast(t('updateAvailable') + ` v${r.latest}`, 'ok');\n else if (r.error) showToast(`${t('checkFailed')}\uFF1A${r.error}`, 'err');\n else showToast(t('upToDate'), 'ok');\n }\n } catch (e) {\n setUpdateInfo({ error: e.message });\n if (manual) showToast(`${t('checkFailed')}\uFF1A${e.message}`, 'err');\n }\n }, [t, showToast]);\n\n useEffect(() => {\n void loadUpdate(false); // \u8FDB\u5165\u9875\u9762\u81EA\u52A8\u68C0\u67E5\uFF1A\u5931\u8D25\u9759\u9ED8\n }, [loadUpdate]);\n\n const onUpgrade = useCallback(async () => {\n setUpdateBusy(true);\n try {\n await upgrade();\n showToast(t('upgradeDone'), 'ok');\n } catch (e) {\n showToast(`${t('upgradeFailed')}\uFF1A${e.message}`, 'err');\n } finally {\n setUpdateBusy(false);\n }\n }, [t, showToast]);\n\n // \u63A8\u8350\u63D2\u4EF6\u52A0\u8F7D\uFF08\u8FDB\u5165\u8BBE\u7F6E\u9875 + \u5B89\u88C5\u540E\u5237\u65B0\uFF09\n const loadRecommended = useCallback(async () => {\n try {\n const r = await getRecommendedPlugins();\n setRecommended(r.plugins || []);\n } catch (e) {\n setRecommended([]); // \u540E\u7AEF\u672A\u5B9E\u73B0/\u5F02\u5E38\uFF1A\u9759\u9ED8\u9690\u85CF\u63A8\u8350\u5361\n }\n }, []);\n\n useEffect(() => {\n void loadRecommended();\n }, [loadRecommended]);\n\n const onInstallPlugin = useCallback(async (id) => {\n setInstallBusy(id);\n setInstallNotice(null);\n try {\n const r = await installPlugin(id);\n setInstallNotice({ id, text: r.rebootNeeded ? t('installReboot') : t('installDone'), kind: 'ok' });\n void loadRecommended();\n } catch (e) {\n setInstallNotice({ id, text: `${t('installFailed')}\uFF1A${e.message}`, kind: 'err' });\n } finally {\n setInstallBusy(null);\n }\n }, [t, loadRecommended]);\n\n const refresh = useCallback(async () => {\n try {\n setStatus(await getStatus());\n } catch (e) {\n setError(e.message);\n }\n }, []);\n\n // \u72B6\u6001\u8F6E\u8BE2\n useEffect(() => {\n void refresh();\n const timer = window.setInterval(() => void refresh(), 2000);\n return () => window.clearInterval(timer);\n }, [refresh]);\n\n // \u914D\u5BF9\u4E2D\u8F6E\u8BE2\uFF1A\u624B\u673A\u63D0\u4EA4\uFF08completed\uFF09\u2192 \u663E\u793A\u786E\u8BA4\uFF1B\u786E\u8BA4\u540E\uFF08done\uFF09\u2192 \u6536\u5C3E\n useEffect(() => {\n if (!pairing || pairing.phase === 'done' || pairing.phase === 'awaiting-confirm') return;\n let alive = true;\n const timer = window.setInterval(async () => {\n try {\n const st = await getPairing(pairing.pairingId);\n if (st.status === 'completed' && alive) {\n // \u624B\u673A\u5DF2\u63D0\u4EA4\uFF08completed\uFF09\uFF1A\u8BB0\u5F55\u624B\u673A\u8EAB\u4EFD\u516C\u94A5\u3001\u8BBE\u5907\u540D\u3001\u77ED\u7801\uFF08A0 \u7801\u6BD4\u5BF9\uFF09\n setPairing((p) => ({\n ...p,\n phase: 'awaiting-confirm',\n phonePk: st.phone?.pk_identity || p.phonePk,\n phoneName: st.phone?.name || p.phoneName,\n shortCode: st.shortCode || null, // A0\uFF1A\u9996\u6B21\u914D\u5BF9\u77ED\u7801\uFF08connector \u672C\u5730\u8BA1\u7B97\uFF09\n known: !!st.known, // A0\uFF1A\u5DF2 pin \u8BBE\u5907 \u2192 \u514D\u7801\u76F4\u63A5\u786E\u8BA4\n }));\n } else if (st.status === 'done' && alive) {\n setPairing((p) => ({ ...p, phase: 'done' }));\n void refresh();\n }\n } catch {}\n }, 1500);\n return () => {\n alive = false;\n window.clearInterval(timer);\n };\n }, [pairing, refresh]);\n\n async function run(label, action) {\n try {\n setBusy(label);\n setError(null);\n setNotice(null);\n await action();\n } catch (e) {\n setError(e.message || t('operationFailed'));\n } finally {\n setBusy(null);\n }\n }\n\n async function onStartPairing() {\n await run('pair', async () => {\n const p = await startPairing();\n setPairing({ ...p, phase: 'waiting-scan' });\n });\n }\n\n async function onConfirm() {\n if (!pairing) return;\n await run('confirm', async () => {\n await confirmPairing(pairing.pairingId, confirmFullDisk);\n setPairing((p) => ({ ...p, phase: 'done' }));\n setConfirmFullDisk(false); // \u590D\u4F4D\uFF0C\u4E0B\u6B21\u914D\u5BF9\u91CD\u65B0\u9009\u62E9\n await refresh();\n });\n }\n\n async function onReject() {\n if (!pairing) return;\n await run('reject', async () => {\n await rejectPairing(pairing.pairingId);\n setPairing(null); // \u62D2\u7EDD\u540E\u56DE\u5230\u521D\u59CB\u6001\uFF08\u53EF\u91CD\u65B0\u65B0\u5EFA\u914D\u5BF9\uFF09\n setNotice(t('pairingRejected'));\n });\n }\n\n async function onUnpair(phonePk) {\n if (!window.confirm(t('confirmUnpair'))) return;\n await run('unpair', async () => {\n await unpair(phonePk);\n await refresh();\n });\n }\n\n async function onTogglePermission(phonePk, fullDisk) {\n await run('perm', async () => {\n await setPermission(phonePk, fullDisk);\n await refresh();\n setNotice(t('permissionChanged'));\n });\n }\n\n async function onCopyLink() {\n if (!pairing?.deepLink) return;\n try {\n await navigator.clipboard.writeText(pairing.deepLink);\n setNotice(t('copied'));\n } catch {\n setError(t('operationFailed'));\n }\n }\n\n // \u4E2D\u7EE7\u5730\u5740\u7F16\u8F91\u5DF2\u79FB\u9664\uFF082026-08 \u5B89\u5168\u6536\u7D27\uFF1A\u9632\u7BE1\u6539\u4E2D\u7EE7\u670D\u52A1\u5668\uFF1B\u6362\u4E2D\u7EE7\u8D70 keys.relayUrl \u6587\u4EF6\u914D\u7F6E\uFF09\n\n const connected = status?.connection === 'connected';\n const devices = status?.devices || [];\n // \u63A8\u8350\u63D2\u4EF6\uFF1A\u53EA\u5C55\u793A\u672A\u5B89\u88C5\u7684\uFF1B\u5168\u90E8\u5DF2\u5B89\u88C5\u5219\u6574\u5361\u9690\u85CF\uFF08\u8FC7\u6EE4\u5DF2\u88C5\u63D2\u4EF6\uFF09\n const recommendedPending = (recommended || []).filter((p) => !p.installed);\n\n return (\n <div style={sectionStyle}>\n {/* \u6807\u9898\u884C\uFF082026-08 \u5BF9\u9F50 DSH \u5185\u7F6E\u8BBE\u7F6E\u9875 .heading\uFF1A18px/600\uFF09+ \u53F3\u4FA7\u7248\u672C\u53F7 + SVG \u68C0\u67E5\u6309\u94AE */}\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>\n <h2 style={{ margin: 0, fontSize: 18, fontWeight: 600, color: 'var(--dsw-alias-label-primary, inherit)' }}>\n {t('nav')}\n </h2>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>\n <code style={{ fontSize: 13, opacity: 0.75 }}>{status?.version ? `v${status.version}` : '\u2026'}</code>\n <button\n title={t('checkUpdate')}\n aria-label={t('checkUpdate')}\n onClick={() => loadUpdate(true)}\n disabled={updateBusy}\n style={{\n border: 'none', background: 'transparent', cursor: updateBusy ? 'default' : 'pointer',\n padding: 6, borderRadius: 6, color: 'inherit', opacity: updateBusy ? 0.4 : 0.7, lineHeight: 0,\n }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" />\n <polyline points=\"21 3 21 9 15 9\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* \u4ECB\u7ECD\uFF08\u5BF9\u9F50 DSH .intro\uFF1A13px + label-tertiary\uFF09 */}\n <p style={{ margin: 0, fontSize: 13, color: 'var(--dsw-alias-label-tertiary, inherit)', lineHeight: 1.6, maxWidth: 560 }}>\n {t('intro')}\n </p>\n\n {/* \u6709\u65B0\u7248\uFF1A\u6761\u4EF6\u6A2A\u5E45\uFF08\u4EC5\u66F4\u65B0\u53EF\u7528\u65F6\u51FA\u73B0\uFF0C\u4E0D\u5F71\u54CD\u5E38\u89C4\u5E03\u5C40\uFF09 */}\n {updateInfo?.updateAvailable && (\n <div style={{ ...cardStyle, borderColor: '#e8890c', padding: '10px 16px' }}>\n <div style={{ ...rowStyle, gap: 10 }}>\n <span style={{ fontSize: 13, color: '#e8890c' }}>\n {t('updateAvailable')} v{updateInfo.latest}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <button style={btnStyle} onClick={onUpgrade} disabled={updateBusy}>\n {updateBusy ? t('upgrading') : t('upgrade')}\n </button>\n </span>\n </div>\n </div>\n )}\n\n {/* \u8FDE\u63A5\u72B6\u6001 */}\n <div style={cardStyle}>\n {/* \u4E2D\u7EE7\u670D\u52A1\u5668\uFF082026-08 \u5B89\u5168\u6536\u7D27\uFF09\uFF1A\u4E0D\u663E\u793A\u5730\u5740\u4E0E\u7F16\u8F91\u6309\u94AE\uFF0C\u53EA\u663E\u793A\u8FDE\u63A5\u72B6\u6001\uFF1B\n \u5730\u5740\u7ECF\u72B6\u6001\u5FBD\u6807\u60AC\u6D6E\u63D0\u793A\uFF08title\uFF09 */}\n <div style={rowStyle}>\n <span style={labelStyle}>{t('relayServer')}</span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n {statusBadge(status?.connection, t, status?.relayUrl)}\n {tokenBadge(status?.tokenState, t)}\n </span>\n </div>\n {/* \u7248\u672C + \u5347\u7EA7\u68C0\u67E5\u5DF2\u79FB\u81F3\u9875\u9762\u6807\u9898\u884C\uFF082026-08 \u5E03\u5C40\u4FEE\u6B63\uFF09\uFF1A\u907F\u514D\u5360\u7528\u8FDE\u63A5\u72B6\u6001\u5361\u8868\u683C\u884C */}\n <div style={rowStyle}>\n <span style={labelStyle}>{t('accountName')}</span>\n <code>{status?.accountName || '\u2026'}</code>\n </div>\n <div style={rowStyle}>\n <span style={labelStyle}>{t('deviceId')}</span>\n <code>{status?.deviceId ? status.deviceId.slice(0, 12) + '\u2026' : '\u2026'}</code>\n </div>\n <div style={rowStyle}>\n <span style={labelStyle}>{t('hostname')}</span>\n <code>{status?.hostname || '\u2026'}</code>\n </div>\n </div>\n\n {/* \u63A8\u8350\u63D2\u4EF6\uFF082026-08\uFF09\uFF1A\u5B98\u65B9\u63D2\u4EF6\u4E00\u952E\u88C5\u5230\u672C\u673A dsh web\uFF08\u767D\u540D\u5355\uFF0C\u70ED\u52A0\u8F7D\uFF09\uFF1B\u53EA\u5C55\u793A\u672A\u5B89\u88C5\u7684\uFF0C\n \u5168\u90E8\u5DF2\u5B89\u88C5\u5219\u6574\u5361\u9690\u85CF\uFF08recommendedPending \u8FC7\u6EE4 installed\uFF09 */}\n {recommendedPending.length > 0 && (\n <div style={cardStyle}>\n <div style={{ fontSize: 15, fontWeight: '600' }}>{t('recommendedTitle')}</div>\n <div style={{ fontSize: 13, opacity: 0.7, lineHeight: 1.5 }}>{t('recommendedHint')}</div>\n {recommendedPending.map((p) => (\n <div key={p.id} style={rowStyle}>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>\n <span>{p.icon ? <span style={{ fontSize: 16 }}>\uD83D\uDCE6</span> : null}</span>\n <span><strong>{p.title}</strong> \u2014 {p.description}</span>\n </span>\n <button\n style={btnStyle}\n onClick={() => onInstallPlugin(p.id)}\n disabled={installBusy !== null}\n >\n {installBusy === p.id ? t('installing') : t('install')}\n </button>\n {installNotice?.id === p.id && (\n <span style={{ fontSize: 12, color: installNotice.kind === 'err' ? '#c92a2a' : '#2f9e44' }}>\n {installNotice.text}\n </span>\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* \u914D\u5BF9 */}\n <div style={cardStyle}>\n <div style={rowStyle}>\n <strong>{t('pairingTitle')}</strong>\n {!pairing && (\n <button style={btnStyle} onClick={onStartPairing} disabled={busy !== null || !connected}>\n {t('startPairing')}\n </button>\n )}\n </div>\n\n {pairing && pairing.phase === 'waiting-scan' && pairing.qrDataUrl && (\n <div style={{ display: 'grid', justifyItems: 'center', gap: 10 }}>\n <img src={pairing.qrDataUrl} alt=\"QR\" style={{ width: 220, height: 220, borderRadius: 8 }} />\n <div style={{ fontSize: 13, opacity: 0.75 }}>{t('scanHint')}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>\n {pairing.pairingId} \u00B7 {pairing.fp.slice(0, 12)}\u2026\n </div>\n <button style={btnStyle} onClick={onCopyLink}>{t('copyLink')}</button>\n </div>\n )}\n\n {pairing && pairing.phase === 'done' && (\n <div style={{ display: 'grid', gap: 10 }}>\n <div style={{ fontSize: 14, color: '#2f9e44' }}>{t('pairingDone')}</div>\n <button style={btnStyle} onClick={() => setPairing(null)} disabled={busy !== null}>\n {t('startPairing')}\n </button>\n </div>\n )}\n </div>\n\n {/* \u914D\u5BF9\u786E\u8BA4\u5F39\u7A97\uFF1A\u624B\u673A\u63D0\u4EA4\u540E\u5F39\u51FA\uFF0C\u663E\u773C\u7684\u786E\u8BA4/\u62D2\u7EDD\uFF08\u4E0D\u518D\u7528\u4E0D\u663E\u773C\u7684\u5185\u8054\u6309\u94AE\uFF09 */}\n {pairing && pairing.phase === 'awaiting-confirm' && (\n <div style={modalBackdropStyle}>\n <div style={modalCardStyle}>\n <div style={modalTitleStyle}>{t('confirmPairing')}</div>\n <div style={{ fontSize: 14, opacity: 0.8, marginBottom: 6 }}>\n {t('awaitingConfirm')}\n </div>\n <div style={{ fontSize: 13, opacity: 0.65, marginBottom: 14 }}>\n {/* \u53EF\u8BFB\u8BBE\u5907\u540D\u4F18\u5148\uFF1B\u65E0\uFF08\u65E7\u624B\u673A\u672A\u63D0\u4EA4\uFF09\u5219\u56DE\u9000\u8BBE\u5907 ID \u77ED\u7801 */}\n {pairing.phoneName\n ? `${t('deviceId')}: ${pairing.phoneName}`\n : `${pairing.pairingId.slice(0, 8)}\u2026 \u00B7 ${t('deviceId')}: ${pairing.phonePk ? pairing.phonePk.slice(0, 10) + '\u2026' : '\u2014'}`}\n </div>\n {/* A0\uFF08S0\uFF09\u914D\u5BF9\u951A\u5B9A\uFF1A\u9996\u6B21\u914D\u5BF9\u5C55\u793A\u77ED\u7801\uFF0C\u4E0E\u624B\u673A\u5C4F\u5E55\u6BD4\u5BF9\u4E00\u81F4\u624D\u786E\u8BA4\uFF08\u4E2D\u7EE7\u65E0\u6CD5\u4F2A\u9020\u4E24\u7AEF\uFF09 */}\n {pairing.shortCode && (\n <div style={{ background: 'var(--dsh-border, rgba(127,127,127,.08))', borderRadius: 8, padding: '12px 14px', marginBottom: 12, textAlign: 'center' }}>\n <div style={{ fontSize: 12, opacity: 0.7, marginBottom: 6 }}>{t('shortCodeHint')}</div>\n <div style={{ fontSize: 28, fontWeight: 800, letterSpacing: 4, fontFamily: 'monospace', color: 'var(--dsh-fg, #1a1d26)' }}>{pairing.shortCode}</div>\n <div style={{ fontSize: 12, opacity: 0.6, marginTop: 6, lineHeight: 1.5 }}>{t('shortCodeCompare')}</div>\n </div>\n )}\n {pairing.known && (\n <div style={{ fontSize: 13, opacity: 0.7, marginBottom: 12 }}>{t('knownDevice')}</div>\n )}\n {/* \u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF082026-08\uFF09\uFF1A\u786E\u8BA4\u65F6\u9009\u62E9\uFF0C\u914D\u5BF9\u540E\u53EF\u5728\u8BBE\u5907\u5217\u8868\u4FEE\u6539 */}\n <label style={{ ...switchStyle, marginBottom: 16, cursor: 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={confirmFullDisk}\n onChange={(e) => setConfirmFullDisk(e.target.checked)}\n disabled={busy !== null}\n />\n <span>{t('fullDisk')}</span>\n </label>\n <div style={{ fontSize: 12, opacity: 0.6, marginBottom: 16, lineHeight: 1.5 }}>{t('fullDiskHint')}</div>\n <div style={{ display: 'flex', gap: 10 }}>\n <button style={{ ...btnStyle, flex: 1, background: '#fff', borderColor: '#c92a2a', color: '#c92a2a', fontWeight: '600', padding: '12px 16px' }} onClick={onReject} disabled={busy !== null}>\n {t('reject')}\n </button>\n <button style={{ ...btnStyle, flex: 2, background: '#2f9e44', borderColor: '#2f9e44', color: '#fff', fontWeight: '700', padding: '12px 16px' }} onClick={onConfirm} disabled={busy !== null}>\n {t('approve')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* \u5DF2\u914D\u5BF9\u8BBE\u5907 */}\n <div style={cardStyle}>\n <strong>{t('pairedDevices')}</strong>\n {devices.length === 0 && <div style={{ fontSize: 13, opacity: 0.6 }}>{t('noDevices')}</div>}\n {devices.map((d) => (\n <div key={d.phonePk} style={{ ...rowStyle, padding: '6px 0', borderTop: '1px solid var(--dsh-border, rgba(127,127,127,.15))' }}>\n <div style={{ display: 'grid', gap: 4 }}>\n {/* \u8BBE\u5907\u540D\uFF08\u624B\u673A\u4E0A\u62A5\uFF0C2026-08\uFF09\uFF1A\u6709\u540D\u5B57\u663E\u793A\u540D\u5B57\uFF0C\u5426\u5219\u56DE\u9000\u77ED\u7801 */}\n <code style={{ fontSize: 13, fontWeight: d.name ? 600 : 400 }}>{d.name || d.shortId + '\u2026'}</code>\n <span style={{ fontSize: 12, opacity: 0.6 }}>\n {t('lastUsed')}: {fmtTime(d.lastUsedAt, t)}\n </span>\n {/* \u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF1A\u663E\u793A + \u4FEE\u6539\uFF082026-08\uFF09 */}\n <label style={{ ...switchStyle, cursor: 'pointer', opacity: 0.9 }}>\n <input\n type=\"checkbox\"\n checked={!!(d.permission && d.permission.fullDisk)}\n onChange={(e) => onTogglePermission(d.phonePk, e.target.checked)}\n disabled={busy !== null}\n />\n <span>{t('fullDisk')}</span>\n </label>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>\n {/* \u8FDE\u63A5\u72B6\u6001\u63D0\u793A\uFF08M5.12\uFF09\uFF1A\u5728\u7EBF\u7EFF / \u79BB\u7EBF\u7070\uFF0C\u4F4D\u4E8E\u89E3\u9664\u914D\u5BF9\u6309\u94AE\u5DE6\u4FA7 */}\n <span style={deviceStatusStyle(d.online !== false)}>\n {d.online === false ? `\u25CB ${t('deviceOffline')}` : `\u25CF ${t('deviceOnline')}`}\n </span>\n <button style={btnStyle} onClick={() => onUnpair(d.phonePk)} disabled={busy !== null}>\n {t('unpair')}\n </button>\n </div>\n </div>\n ))}\n </div>\n\n {notice && <div style={{ fontSize: 13, color: '#2f9e44' }}>{notice}</div>}\n {error && <div style={{ fontSize: 13, color: '#c92a2a' }}>{error}</div>}\n\n {/* \u5F31\u5F39\u7A97\uFF082026-08\uFF1A\u68C0\u67E5\u66F4\u65B0\u5931\u8D25/\u5DF2\u6700\u65B0\u3001\u5347\u7EA7\u7ED3\u679C\uFF1Bfixed \u53F3\u4E0A\u89D2\uFF0C3s \u81EA\u52A8\u6D88\u5931\uFF0C\u4E0D\u5E38\u9A7B\uFF09 */}\n {toast && (\n <div style={{\n position: 'fixed', top: 24, right: 24, zIndex: 10000,\n background: toast.kind === 'err' ? '#c92a2a' : '#2f9e44',\n color: '#fff', borderRadius: 8, padding: '10px 16px', fontSize: 13,\n boxShadow: '0 8px 24px rgba(0,0,0,.25)', maxWidth: 320, lineHeight: 1.5,\n }}>\n {toast.text}\n </div>\n )}\n </div>\n );\n}\n", "// packages/remote-connector/src/client/api.js\n// /dshfly \u540E\u7AEF\u8DEF\u7531\u7684\u524D\u7AEF\u5C01\u88C5\uFF08\u540C\u6E90\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u6D4F\u89C8\u5668 fetch\uFF09\u3002\n// \u8DEF\u7531\u5B9E\u73B0\u89C1 ../../http-api.js\uFF1Bloopback \u6805\u680F\u8981\u6C42 Host \u4E3A 127.0.0.1\uFF08dsh web \u6052\u7ED1 loopback\uFF0C\n// \u6D4F\u89C8\u5668\u540C\u6E90\u8BF7\u6C42\u5929\u7136\u6EE1\u8DB3\uFF1BPOST \u5E26\u540C\u6E90 Origin \u4EA6\u901A\u8FC7\uFF09\u3002\n\nasync function request(path, init) {\n const headers = new Headers(init?.headers);\n if (init?.body !== undefined && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n const response = await fetch(path, { ...init, headers, cache: 'no-store' });\n let payload = null;\n try {\n payload = await response.json();\n } catch {}\n if (!response.ok) {\n throw new Error((payload && payload.error) || `HTTP ${response.status}`);\n }\n return payload;\n}\n\nexport const getStatus = () => request('/dshfly/status');\nexport const startPairing = () => request('/dshfly/pair', { method: 'POST' });\nexport const getPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}`);\nexport const confirmPairing = (id, fullDisk = false) =>\n request(`/dshfly/pair/${encodeURIComponent(id)}/confirm`, { method: 'POST', body: JSON.stringify({ fullDisk }) });\nexport const rejectPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}/reject`, { method: 'POST' });\nexport const unpair = (phonePk) => request('/dshfly/unpair', { method: 'POST', body: JSON.stringify({ phonePk }) });\nexport const setPermission = (phonePk, fullDisk) =>\n request('/dshfly/permissions', { method: 'POST', body: JSON.stringify({ phonePk, fullDisk }) });\nexport const getRecommendedPlugins = () => request('/dshfly/plugins/recommended');\nexport const installPlugin = (id) =>\n request('/dshfly/plugins/install', { method: 'POST', body: JSON.stringify({ id }) });\nexport const checkUpdate = () => request('/dshfly/plugins/checkUpdate');\nexport const upgrade = () => request('/dshfly/plugins/upgrade', { method: 'POST' });\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["// packages/remote-connector/src/client/index.jsx\n// @dshfly/remote-connector \u7684 DSH web client \u5165\u53E3\uFF08\u6D4F\u89C8\u5668\u4FA7\uFF09\u3002\n// \u5951\u7EA6\uFF08\u5BF9\u9F50 orbis @orbisapp/remote-dsh/src/client/index.tsx\uFF09\uFF1A\n// exports[\"./client\"] \u6307\u5411\u6784\u5EFA\u4EA7\u7269\uFF1Bdsh.client.inject \u58F0\u660E\u4F9D\u8D56\uFF1B\n// apply(ClientContext) \u6CE8\u518C\u8BCD\u5178 + \u5411\u8BBE\u7F6E\u7A97\u53E3\u6CE8\u518C\u72EC\u7ACB section\uFF08\u51FA\u73B0\u5728\u8BBE\u7F6E\u5DE6\u4FA7\u83DC\u5355\uFF09\u3002\n\nimport { zh, en } from './locales';\nimport { SettingsSection } from './SettingsSection';\n\nconst namespace = 'dshfly';\n\nexport const inject = ['slots', 'locale'];\n\nexport function apply(context) {\n context.effect(\n () => context.locale.register(namespace, { zh, en }),\n 'dshfly-remote-connector: dictionaries',\n );\n const translate = context.locale.bind(namespace);\n const injected = () => ({ t: translate });\n // settings.section\uFF08list slot\uFF09\uFF1A\u8BBE\u7F6E\u7A97\u53E3\u5DE6\u4FA7\u83DC\u5355\u7684\u6570\u636E\u6E90\u2014\u2014\u6BCF\u4E2A entry \u4E00\u4E2A\u83DC\u5355\u9879\uFF0C\n // \u70B9\u51FB\u540E\u53F3\u4FA7\u5185\u5BB9\u533A\u6E32\u67D3\u5BF9\u5E94\u7EC4\u4EF6\uFF08props: { close } + inject \u7684 { t }\uFF09\u3002\n // \u81EA settings.plugins.tab\uFF08\"\u8BBE\u7F6E-\u63D2\u4EF6\" tab \u5185\uFF09\u8FC1\u5165\uFF082026-08\uFF09\uFF1A\u914D\u5BF9\u9762\u677F\u6210\u4E3A\u72EC\u7ACB\u83DC\u5355\u9879\u3002\n context.slots.inject('settings.section', () =>\n context.slots.register(\n {\n name: 'settings.section',\n id: 'dshfly-remote-connector',\n order: 30, // \u4F4D\u4E8E\u5185\u7F6E models(10) \u7B49\u4E4B\u540E\n label: () => translate('nav'),\n inject: injected,\n },\n SettingsSection,\n ),\n );\n}\n", "// packages/remote-connector/src/client/locales.js\n// \u8BBE\u7F6E\u9875\u8BCD\u5178\uFF08zh/en\uFF0Clocale.register \u53CC\u8BED\u6587\u6863\u8981\u6C42\u5E73\u8861\uFF09\u3002\n\nexport const zh = {\n nav: '\u8FDC\u7A0B\u8FDE\u63A5',\n statusConnected: '\u5DF2\u8FDE\u63A5',\n statusConnecting: '\u8FDE\u63A5\u4E2D',\n statusDisconnected: '\u672A\u8FDE\u63A5',\n relayServer: '\u4E2D\u7EE7\u670D\u52A1\u5668',\n version: '\u7248\u672C',\n checkUpdate: '\u68C0\u67E5\u66F4\u65B0',\n updateAvailable: '\u53D1\u73B0\u65B0\u7248\u672C',\n upgrade: '\u4E00\u952E\u5347\u7EA7',\n upgrading: '\u5347\u7EA7\u4E2D\u2026',\n upgradeDone: '\u5347\u7EA7\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F dsh web \u751F\u6548',\n upgradeFailed: '\u5347\u7EA7\u5931\u8D25',\n upToDate: '\u5DF2\u662F\u6700\u65B0\u7248\u672C',\n checkFailed: '\u68C0\u67E5\u66F4\u65B0\u5931\u8D25',\n localAhead: '\u5F53\u524D\u4E3A\u672C\u5730\u5F00\u53D1\u7248\uFF0C\u6BD4 npm \u4E0A\u7684\u7248\u672C\u66F4\u65B0\uFF08\u4E0D\u4F1A\u964D\u7EA7\uFF09',\n localAheadShort: '\u672C\u5730\u5F00\u53D1\u7248\uFF08\u65E0\u66F4\u65B0\uFF09',\n depsBroken: '\u63D2\u4EF6\u4F9D\u8D56\u7F3A\u5931\uFF0Cdsh web \u53EF\u80FD\u65E0\u6CD5\u542F\u52A8',\n depsMissing: '\u7F3A\u5C11\uFF1A',\n depsFix: '\u8BF7\u5728\u63D2\u4EF6\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C pnpm install\uFF0C\u7136\u540E\u91CD\u542F dsh web\u3002',\n accountName: '\u8D26\u53F7',\n deviceId: '\u8BBE\u5907 ID',\n hostname: '\u8BA1\u7B97\u673A\u540D',\n pairedDevices: '\u5DF2\u914D\u5BF9\u8BBE\u5907',\n noDevices: '\u6682\u65E0\u5DF2\u914D\u5BF9\u8BBE\u5907',\n lastUsed: '\u6700\u8FD1\u4F7F\u7528',\n never: '\u4ECE\u672A',\n unpair: '\u89E3\u9664\u914D\u5BF9',\n deviceOnline: '\u5728\u7EBF',\n deviceOffline: '\u79BB\u7EBF',\n startPairing: '\u65B0\u5EFA\u914D\u5BF9',\n pairingTitle: '\u914D\u5BF9\u4E8C\u7EF4\u7801',\n waitingScan: '\u7B49\u5F85\u624B\u673A\u626B\u7801\u2026\uFF0810 \u5206\u949F\u6709\u6548\uFF09',\n recommendedTitle: '\u63A8\u8350\u63D2\u4EF6',\n recommendedHint: '\u88C5\u5230\u672C\u673A dsh web \u540E\uFF0C\u624B\u673A App \u7684\u63D2\u4EF6 Tab \u4F1A\u81EA\u52A8\u51FA\u73B0\uFF1A',\n install: '\u4E00\u952E\u5B89\u88C5',\n installing: '\u5B89\u88C5\u4E2D\u2026',\n installed: '\u5DF2\u5B89\u88C5',\n installDone: '\u5B89\u88C5\u5B8C\u6210\uFF0C\u65E0\u9700\u91CD\u542F',\n installReboot: '\u5DF2\u5B89\u88C5\uFF0C\u9700\u91CD\u542F dsh web \u540E\u751F\u6548',\n installFailed: '\u5B89\u88C5\u5931\u8D25',\n awaitingConfirm: '\u6709\u4E00\u53F0\u624B\u673A\u8BF7\u6C42\u4E0E\u672C\u673A\u914D\u5BF9',\n confirmPairing: '\u914D\u5BF9\u8BF7\u6C42',\n approve: '\u786E\u8BA4\u901A\u8FC7',\n reject: '\u62D2\u7EDD',\n pairingDone: '\u914D\u5BF9\u5B8C\u6210',\n pairingRejected: '\u5DF2\u62D2\u7EDD\u8BE5\u914D\u5BF9\u8BF7\u6C42',\n copyLink: '\u590D\u5236\u94FE\u63A5',\n copied: '\u5DF2\u590D\u5236',\n refresh: '\u5237\u65B0',\n operationFailed: '\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5',\n loadFailed: '\u52A0\u8F7D\u72B6\u6001\u5931\u8D25',\n confirmUnpair: '\u786E\u8BA4\u89E3\u9664\u8FD9\u53F0\u8BBE\u5907\u7684\u914D\u5BF9\uFF1F',\n fullDisk: '\u5141\u8BB8\u6D4F\u89C8\u5B8C\u6574\u78C1\u76D8',\n fullDiskHint: '\u5F00\u542F\u540E\u624B\u673A\u53EF\u6D4F\u89C8\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u5E76\u65B0\u5EFA\u4EFB\u610F\u4F4D\u7F6E\u7684\u5DE5\u4F5C\u533A\uFF1B\u5173\u95ED\u5219\u4EC5\u9650\u5DF2\u767B\u8BB0\u5DE5\u4F5C\u533A\u4E0E dsh web \u4E3B\u76EE\u5F55',\n permissionChanged: '\u6743\u9650\u5DF2\u66F4\u65B0',\n intro: '\u901A\u8FC7\u624B\u673A App \u968F\u65F6\u968F\u5730\u8FDE\u63A5 DeepSeek Harness\u2014\u2014\u7AEF\u5230\u7AEF\u52A0\u5BC6\u3001\u5B89\u5168\u4E2D\u7EE7\uFF0C\u590D\u6742\u7F51\u7EDC\u73AF\u5883\u4E5F\u80FD\u7A33\u5B9A\u8FDE\u63A5\u3002',\n confirm: '\u786E\u5B9A',\n cancel: '\u53D6\u6D88',\n scanHint: '\u5728\u624B\u673A\u4E0A\u4F7F\u7528 DSH Fly \u626B\u7801',\n shortCodeHint: '\u8BF7\u6838\u5BF9\u624B\u673A\u5C4F\u5E55\u4E0A\u7684\u914D\u5BF9\u7801',\n shortCodeCompare: '\u4E0E\u624B\u673A\u300C\u914D\u5BF9\u5B8C\u6210\u300D\u9875\u663E\u793A\u7684 8 \u4F4D\u7801\u4E00\u81F4\u624D\u53EF\u786E\u8BA4\u2014\u2014\u7801\u4E0D\u540C\u8BF4\u660E\u4E2D\u7EE7\u88AB\u7BE1\u6539',\n knownDevice: '\u8FD9\u662F\u5DF2\u914D\u5BF9\u8FC7\u7684\u8BBE\u5907\uFF0C\u786E\u8BA4\u540E\u514D\u7801\u91CD\u8FDE',\n tokenRevoked: '\u8BBE\u5907\u5DF2\u64A4\u9500',\n tokenRevokedTip: '\u672C\u673A\u5DF2\u88AB\u4E2D\u7EE7\u64A4\u9500\uFF0C\u9700\u91CD\u65B0\u521D\u59CB\u5316\u540E\u518D\u914D\u5BF9',\n tokenExpired: '\u51ED\u8BC1\u5DF2\u8FC7\u671F',\n tokenExpiredTip: '\u8BBE\u5907\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u81EA\u52A8\u5237\u65B0\uFF08\u4E00\u822C\u51E0\u79D2\u5185\u6062\u590D\uFF09',\n tokenExpiring: '\u51ED\u8BC1\u5373\u5C06\u8FC7\u671F',\n tokenExpiringTip: '\u8BBE\u5907\u51ED\u8BC1\u5C06\u5728 12 \u5C0F\u65F6\u5185\u8FC7\u671F\uFF0C\u4F1A\u81EA\u52A8\u5237\u65B0',\n tokenUnregistered: '\u672A\u767B\u8BB0',\n tokenUnregisteredTip: '\u672C\u673A\u5C1A\u672A\u5728\u4E2D\u7EE7\u767B\u8BB0\uFF0C\u65B0\u5EFA\u914D\u5BF9\u4F1A\u81EA\u52A8\u767B\u8BB0',\n};\n\nexport const en = {\n nav: 'Remote Connector',\n statusConnected: 'Connected',\n statusConnecting: 'Connecting',\n statusDisconnected: 'Disconnected',\n relayServer: 'Relay server',\n version: 'Version',\n checkUpdate: 'Check update',\n updateAvailable: 'New version available',\n upgrade: 'Upgrade',\n upgrading: 'Upgrading\u2026',\n upgradeDone: 'Upgrade done, restart dsh web to apply',\n upgradeFailed: 'Upgrade failed',\n upToDate: 'Up to date',\n checkFailed: 'Update check failed',\n localAhead: 'Local dev build is newer than the npm release (downgrade blocked)',\n localAheadShort: 'Local dev build (no update)',\n depsBroken: 'Plugin dependencies missing \u2014 dsh web may fail to start',\n depsMissing: 'Missing: ',\n depsFix: 'Run pnpm install at the plugin repo root, then restart dsh web.',\n accountName: 'Account',\n deviceId: 'Device ID',\n hostname: 'Hostname',\n pairedDevices: 'Paired devices',\n noDevices: 'No paired devices',\n lastUsed: 'Last used',\n never: 'never',\n unpair: 'Unpair',\n deviceOnline: 'Online',\n deviceOffline: 'Offline',\n startPairing: 'New pairing',\n pairingTitle: 'Pairing QR',\n waitingScan: 'Waiting for phone scan\u2026 (10 min)',\n awaitingConfirm: 'A phone is requesting to pair with this machine',\n confirmPairing: 'Pairing request',\n approve: 'Approve',\n reject: 'Reject',\n pairingDone: 'Paired',\n pairingRejected: 'Pairing request rejected',\n copyLink: 'Copy link',\n copied: 'Copied',\n refresh: 'Refresh',\n operationFailed: 'Operation failed, retry',\n loadFailed: 'Failed to load status',\n confirmUnpair: 'Unpair this device?',\n fullDisk: 'Allow full disk browsing',\n fullDiskHint: 'When on, the phone can browse the whole filesystem and create workspaces anywhere; when off, it is limited to registered workspaces and the dsh web home directory',\n permissionChanged: 'Permission updated',\n intro: 'Connect to DeepSeek Harness from your phone anytime, anywhere \u2014 end-to-end encrypted, secure relay, stable under any network conditions.',\n confirm: 'OK',\n cancel: 'Cancel',\n scanHint: 'Scan with the DSH Fly app on your phone',\n shortCodeHint: 'Compare with the pairing code on the phone screen',\n shortCodeCompare: 'Only confirm if it matches the 8-char code on the phone\\'s \"paired\" screen \u2014 a mismatch means the relay was tampered with',\n knownDevice: 'This device was paired before; confirm to reconnect without a code',\n tokenRevoked: 'Device revoked',\n tokenRevokedTip: 'This machine was revoked by the relay; reinitialize before pairing',\n tokenExpired: 'Credential expired',\n tokenExpiredTip: 'Device credential expired, auto-refreshing (usually recovers in seconds)',\n tokenExpiring: 'Credential expiring',\n tokenExpiringTip: 'Device credential expires within 12h, auto-refreshed',\n tokenUnregistered: 'Unregistered',\n tokenUnregisteredTip: 'Not registered on the relay; a new pairing registers it automatically',\n recommendedTitle: 'Recommended plugins',\n recommendedHint: 'Installed into this dsh web, they appear in the app plugin tab automatically:',\n install: 'Install',\n installing: 'Installing\u2026',\n installed: 'Installed',\n installDone: 'Installed, no restart needed',\n installReboot: 'Installed, restart dsh web to apply',\n installFailed: 'Install failed',\n};\n", "// packages/remote-connector/src/client/SettingsSection.jsx\n// dsh web \u8BBE\u7F6E\u9875\"\u63D2\u4EF6\"tab \u7684\u914D\u5BF9\u9762\u677F\uFF1A\u8FDE\u63A5\u72B6\u6001 / \u914D\u5BF9\u4E8C\u7EF4\u7801 / \u8BBE\u5907\u7BA1\u7406 / \u4E8C\u6B21\u786E\u8BA4\u3002\n// \u96F6\u914D\u7F6E\uFF1ArelayUrl/\u8D26\u53F7\u5747\u6765\u81EA\u63D2\u4EF6\u540E\u7AEF\uFF08/dshfly/status\uFF09\uFF0C\u672C\u9762\u677F\u53EA\u505A\u5C55\u793A\u4E0E\u64CD\u4F5C\u3002\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\nimport { getStatus, startPairing, getPairing, confirmPairing, rejectPairing, unpair, setPermission, getRecommendedPlugins, installPlugin, checkUpdate, upgrade, doctor } from './api';\n\nconst sectionStyle = {\n display: 'grid',\n gap: 20,\n maxWidth: 760, // \u5BF9\u9F50 DSH \u5185\u7F6E\u8BBE\u7F6E\u9875 section\uFF08.pbvGtq_section max-width:760px\uFF09\n padding: '8px 0 32px',\n};\n\nconst cardStyle = {\n border: '1px solid var(--dsh-border, rgba(127, 127, 127, .28))',\n borderRadius: 12,\n padding: 16,\n display: 'grid',\n gap: 12,\n};\n\nconst rowStyle = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n flexWrap: 'wrap',\n};\n\nconst labelStyle = { fontSize: 12, opacity: 0.65 };\n\nconst badgeStyle = (color) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n fontSize: 13,\n padding: '3px 10px',\n borderRadius: 999,\n border: '1px solid currentColor',\n color,\n});\n\nconst btnStyle = {\n border: '1px solid var(--dsh-border, rgba(127, 127, 127, .35))',\n borderRadius: 8,\n background: 'transparent',\n color: 'inherit',\n padding: '8px 14px',\n cursor: 'pointer',\n fontSize: 13,\n};\n\n// \u6BCF\u53F0\u914D\u5BF9\u8BBE\u5907\u7684\u8FDE\u63A5\u72B6\u6001\u63D0\u793A\uFF08M5.12 \u65B9\u6848C\uFF1A\u624B\u673A /events \u5728\u7EBF\u72B6\u6001\uFF0C\u8BBE\u5907\u5217\u8868\"\u89E3\u9664\u914D\u5BF9\"\u6309\u94AE\u5DE6\u4FA7\uFF09\nconst deviceStatusStyle = (online) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 5,\n fontSize: 12,\n padding: '2px 8px',\n borderRadius: 999,\n border: '1px solid currentColor',\n color: online ? '#2f9e44' : '#868e96',\n whiteSpace: 'nowrap',\n});\n\n// \u6743\u9650\u5F00\u5173\uFF08\u786E\u8BA4\u5F39\u7A97 / \u8BBE\u5907\u5217\u8868\u5171\u7528\uFF09\uFF1Acheckbox \u6837\u5F0F\nconst switchStyle = { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 };\n\n// \u914D\u5BF9\u786E\u8BA4\u5F39\u7A97\uFF08\u5C45\u4E2D\u906E\u7F69\uFF0C\u786E\u8BA4/\u62D2\u7EDD\u6309\u94AE\u9192\u76EE\uFF09\nconst modalBackdropStyle = {\n position: 'fixed', inset: 0, zIndex: 9999,\n background: 'rgba(0, 0, 0, 0.55)',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24,\n};\nconst modalCardStyle = {\n background: 'var(--dsh-card-bg, #fff)',\n color: 'var(--dsh-card-fg, #1a1d26)',\n borderRadius: 16, padding: 24, maxWidth: 380, width: '100%',\n boxShadow: '0 12px 40px rgba(0,0,0,.35)',\n};\nconst modalTitleStyle = { fontSize: 17, fontWeight: '700', marginBottom: 10 };\n\nfunction statusBadge(connection, t, relayUrl) {\n // title\uFF1A\u60AC\u6D6E\u63D0\u793A\u4E2D\u7EE7\u670D\u52A1\u5668\u5730\u5740\uFF08\u4E0D\u76F4\u63A5\u5C55\u793A\uFF0C\u9632\u7BE1\u6539\u66B4\u9732\u9762\uFF1B2026-08 \u5B89\u5168\u6536\u7D27\uFF09\n const tip = relayUrl ? `${t('relayServer')}: ${relayUrl}` : undefined;\n if (connection === 'connected') return <span style={badgeStyle('#2f9e44')} title={tip}>\u25CF {t('statusConnected')}</span>;\n if (connection === 'connecting') return <span style={badgeStyle('#e8890c')} title={tip}>\u25D0 {t('statusConnecting')}</span>;\n return <span style={badgeStyle('#c92a2a')} title={tip}>\u25CB {t('statusDisconnected')}</span>;\n}\n\n// device token \u5065\u5EB7\u72B6\u6001\uFF082026-08 \u81EA\u6108\uFF09\uFF1A\u63D0\u524D\u9884\u8B66\"\u5373\u5C06\u8FC7\u671F/\u5DF2\u8FC7\u671F/\u5DF2\u64A4\u9500\"\uFF0C\u800C\u975E 401 \u624D\u53BB\u731C\nfunction tokenBadge(tokenState, t) {\n // expiringAt \u53EA\u505A\u60AC\u6D6E\u63D0\u793A\uFF08\u771F\u5B9E\u72B6\u6001\u81EA\u6108\u540E\u81EA\u52A8\u7FFB\u8F6C\uFF0C\u4E0D\u9700\u5C55\u793A\u5177\u4F53\u65F6\u95F4\uFF09\n if (tokenState === 'revoked') return <span style={badgeStyle('#c92a2a')} title={t('tokenRevokedTip')}>\u26A0 {t('tokenRevoked')}</span>;\n if (tokenState === 'expired') return <span style={badgeStyle('#c92a2a')} title={t('tokenExpiredTip')}>\u26A0 {t('tokenExpired')}</span>;\n if (tokenState === 'expiring') return <span style={badgeStyle('#e8890c')} title={t('tokenExpiringTip')}>\u25D0 {t('tokenExpiring')}</span>;\n if (tokenState === 'unregistered') return <span style={badgeStyle('#868e96')} title={t('tokenUnregisteredTip')}>\u25CB {t('tokenUnregistered')}</span>;\n // 'ok' / 'unknown' / \u7F3A\u7701\uFF1A\u4E0D\u8BEF\u5BFC\n return null;\n}\n\nfunction fmtTime(ts, t) {\n if (!ts) return t('never');\n const d = new Date(ts);\n return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;\n}\n\nexport function SettingsSection({ t }) {\n const [status, setStatus] = useState(null);\n const [pairing, setPairing] = useState(null); // {pairingId, fp, deepLink, qrDataUrl, phase, phonePk?}\n const [confirmFullDisk, setConfirmFullDisk] = useState(false); // \u914D\u5BF9\u786E\u8BA4\u65F6\u7684\u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF08\u9ED8\u8BA4\u5173\uFF09\n const [busy, setBusy] = useState(null);\n const [error, setError] = useState(null);\n const [notice, setNotice] = useState(null);\n const [recommended, setRecommended] = useState(null); // \u63A8\u8350\u63D2\u4EF6\u5217\u8868\uFF08null = \u672A\u52A0\u8F7D\uFF09\n const [installBusy, setInstallBusy] = useState(null); // \u6B63\u5728\u5B89\u88C5\u7684\u63D2\u4EF6 id\n const [installNotice, setInstallNotice] = useState(null); // {id, text, kind}\n const [updateInfo, setUpdateInfo] = useState(null); // {current, latest, updateAvailable, localAhead, error?} \u5347\u7EA7\u68C0\u67E5\n const [updateBusy, setUpdateBusy] = useState(false);\n const [doctorInfo, setDoctorInfo] = useState(null); // \u4F9D\u8D56\u81EA\u68C0\uFF082026-09\uFF09\uFF1A\u7F3A\u5931\u65F6\u63D0\u793A pnpm install\n const [toast, setToast] = useState(null); // {text, kind} \u5F31\u5F39\u7A97\uFF08\u81EA\u52A8\u6D88\u5931\uFF09\n\n // \u5F31\u5F39\u7A97\uFF1A\u663E\u793A\u540E 3s \u81EA\u52A8\u6D88\u5931\uFF08\u68C0\u67E5\u5931\u8D25/\u5347\u7EA7\u7ED3\u679C\u7528\uFF0C\u4E0D\u5E38\u9A7B\u754C\u9762\uFF09\n const toastTimer = useRef(null);\n const showToast = useCallback((text, kind = 'ok') => {\n setToast({ text, kind });\n if (toastTimer.current) clearTimeout(toastTimer.current);\n toastTimer.current = setTimeout(() => setToast(null), 3000);\n }, []);\n\n // \u5347\u7EA7\u68C0\u67E5\uFF1Amanual=false\uFF08\u8FDB\u5165\u9875\u9762\u81EA\u52A8\uFF09\u5931\u8D25\u9759\u9ED8\uFF1Bmanual=true\uFF08\u7528\u6237\u70B9\u51FB\uFF09\u5931\u8D25/\u5DF2\u6700\u65B0\u8D70\u5F31\u5F39\u7A97\n const loadUpdate = useCallback(async (manual = false) => {\n try {\n const r = await checkUpdate();\n setUpdateInfo(r);\n if (manual) {\n if (r.updateAvailable) showToast(t('updateAvailable') + ` v${r.latest}`, 'ok');\n else if (r.error) showToast(`${t('checkFailed')}\uFF1A${r.error}`, 'err');\n // 2026-09\uFF1A\u672C\u5730\u5F00\u53D1\u7248\u6BD4 npm \u65B0 \u2192 \u660E\u786E\u8BF4\u660E\"\u65E0\u66F4\u65B0\u3001\u4E0D\u4F1A\u964D\u7EA7\"\uFF0C\u907F\u514D\u4EE5\u4E3A\u68C0\u67E5\u574F\u4E86\n else if (r.localAhead) showToast(t('localAhead'), 'ok');\n else showToast(t('upToDate'), 'ok');\n }\n } catch (e) {\n setUpdateInfo({ error: e.message });\n if (manual) showToast(`${t('checkFailed')}\uFF1A${e.message}`, 'err');\n }\n }, [t, showToast]);\n\n useEffect(() => {\n void loadUpdate(false); // \u8FDB\u5165\u9875\u9762\u81EA\u52A8\u68C0\u67E5\uFF1A\u5931\u8D25\u9759\u9ED8\n }, [loadUpdate]);\n\n // \u4F9D\u8D56\u81EA\u68C0\uFF082026-09\uFF09\uFF1A\u8FDB\u5165\u9875\u9762\u62C9\u4E00\u6B21\uFF1B\u5931\u8D25\u9759\u9ED8\uFF08\u65E7\u63D2\u4EF6\u65E0\u6B64\u7AEF\u70B9\uFF09\n useEffect(() => {\n let alive = true;\n void (async () => {\n try {\n const r = await doctor();\n if (alive) setDoctorInfo(r);\n } catch {\n if (alive) setDoctorInfo(null);\n }\n })();\n return () => { alive = false; };\n }, []);\n\n const onUpgrade = useCallback(async () => {\n setUpdateBusy(true);\n try {\n await upgrade();\n showToast(t('upgradeDone'), 'ok');\n void loadUpdate(false); // \u5347\u7EA7\u6210\u529F\u540E\u5237\u65B0\u7248\u672C/\u6A2A\u5E45\u72B6\u6001\n } catch (e) {\n // \u670D\u52A1\u7AEF\u4F1A\u5E26\u4E0A\"\u4E3A\u4EC0\u4E48\u4E0D\u80FD\u5347\"\u7684\u5B8C\u6574\u89E3\u91CA\uFF08\u5982\u672C\u5730\u5F00\u53D1\u7248\u66F4\u9AD8\u3001registry \u4E0D\u53EF\u8FBE\uFF09\u2014\u2014\u539F\u6837\u5C55\u793A\uFF0C\u522B\u541E\u6389\n showToast(`${t('upgradeFailed')}\uFF1A${e.message}`, 'err');\n void loadUpdate(false); // \u91CD\u65B0\u6838\u5BF9\u72B6\u6001\uFF08\u9632\u964D\u7EA7\u88AB\u62E6\u540E\u5C31\u8BE5\u6CA1\u6709\u6A2A\u5E45\u4E86\uFF09\n } finally {\n setUpdateBusy(false);\n }\n }, [t, showToast, loadUpdate]);\n\n // \u63A8\u8350\u63D2\u4EF6\u52A0\u8F7D\uFF08\u8FDB\u5165\u8BBE\u7F6E\u9875 + \u5B89\u88C5\u540E\u5237\u65B0\uFF09\n const loadRecommended = useCallback(async () => {\n try {\n const r = await getRecommendedPlugins();\n setRecommended(r.plugins || []);\n } catch (e) {\n setRecommended([]); // \u540E\u7AEF\u672A\u5B9E\u73B0/\u5F02\u5E38\uFF1A\u9759\u9ED8\u9690\u85CF\u63A8\u8350\u5361\n }\n }, []);\n\n useEffect(() => {\n void loadRecommended();\n }, [loadRecommended]);\n\n const onInstallPlugin = useCallback(async (id) => {\n setInstallBusy(id);\n setInstallNotice(null);\n try {\n const r = await installPlugin(id);\n setInstallNotice({ id, text: r.rebootNeeded ? t('installReboot') : t('installDone'), kind: 'ok' });\n void loadRecommended();\n } catch (e) {\n setInstallNotice({ id, text: `${t('installFailed')}\uFF1A${e.message}`, kind: 'err' });\n } finally {\n setInstallBusy(null);\n }\n }, [t, loadRecommended]);\n\n const refresh = useCallback(async () => {\n try {\n setStatus(await getStatus());\n } catch (e) {\n setError(e.message);\n }\n }, []);\n\n // \u72B6\u6001\u8F6E\u8BE2\n useEffect(() => {\n void refresh();\n const timer = window.setInterval(() => void refresh(), 2000);\n return () => window.clearInterval(timer);\n }, [refresh]);\n\n // \u914D\u5BF9\u4E2D\u8F6E\u8BE2\uFF1A\u624B\u673A\u63D0\u4EA4\uFF08completed\uFF09\u2192 \u663E\u793A\u786E\u8BA4\uFF1B\u786E\u8BA4\u540E\uFF08done\uFF09\u2192 \u6536\u5C3E\n useEffect(() => {\n if (!pairing || pairing.phase === 'done' || pairing.phase === 'awaiting-confirm') return;\n let alive = true;\n const timer = window.setInterval(async () => {\n try {\n const st = await getPairing(pairing.pairingId);\n if (st.status === 'completed' && alive) {\n // \u624B\u673A\u5DF2\u63D0\u4EA4\uFF08completed\uFF09\uFF1A\u8BB0\u5F55\u624B\u673A\u8EAB\u4EFD\u516C\u94A5\u3001\u8BBE\u5907\u540D\u3001\u77ED\u7801\uFF08A0 \u7801\u6BD4\u5BF9\uFF09\n setPairing((p) => ({\n ...p,\n phase: 'awaiting-confirm',\n phonePk: st.phone?.pk_identity || p.phonePk,\n phoneName: st.phone?.name || p.phoneName,\n shortCode: st.shortCode || null, // A0\uFF1A\u9996\u6B21\u914D\u5BF9\u77ED\u7801\uFF08connector \u672C\u5730\u8BA1\u7B97\uFF09\n known: !!st.known, // A0\uFF1A\u5DF2 pin \u8BBE\u5907 \u2192 \u514D\u7801\u76F4\u63A5\u786E\u8BA4\n }));\n } else if (st.status === 'done' && alive) {\n setPairing((p) => ({ ...p, phase: 'done' }));\n void refresh();\n }\n } catch {}\n }, 1500);\n return () => {\n alive = false;\n window.clearInterval(timer);\n };\n }, [pairing, refresh]);\n\n async function run(label, action) {\n try {\n setBusy(label);\n setError(null);\n setNotice(null);\n await action();\n } catch (e) {\n setError(e.message || t('operationFailed'));\n } finally {\n setBusy(null);\n }\n }\n\n async function onStartPairing() {\n await run('pair', async () => {\n const p = await startPairing();\n setPairing({ ...p, phase: 'waiting-scan' });\n });\n }\n\n async function onConfirm() {\n if (!pairing) return;\n await run('confirm', async () => {\n await confirmPairing(pairing.pairingId, confirmFullDisk);\n setPairing((p) => ({ ...p, phase: 'done' }));\n setConfirmFullDisk(false); // \u590D\u4F4D\uFF0C\u4E0B\u6B21\u914D\u5BF9\u91CD\u65B0\u9009\u62E9\n await refresh();\n });\n }\n\n async function onReject() {\n if (!pairing) return;\n await run('reject', async () => {\n await rejectPairing(pairing.pairingId);\n setPairing(null); // \u62D2\u7EDD\u540E\u56DE\u5230\u521D\u59CB\u6001\uFF08\u53EF\u91CD\u65B0\u65B0\u5EFA\u914D\u5BF9\uFF09\n setNotice(t('pairingRejected'));\n });\n }\n\n async function onUnpair(phonePk) {\n if (!window.confirm(t('confirmUnpair'))) return;\n await run('unpair', async () => {\n await unpair(phonePk);\n await refresh();\n });\n }\n\n async function onTogglePermission(phonePk, fullDisk) {\n await run('perm', async () => {\n await setPermission(phonePk, fullDisk);\n await refresh();\n setNotice(t('permissionChanged'));\n });\n }\n\n async function onCopyLink() {\n if (!pairing?.deepLink) return;\n try {\n await navigator.clipboard.writeText(pairing.deepLink);\n setNotice(t('copied'));\n } catch {\n setError(t('operationFailed'));\n }\n }\n\n // \u4E2D\u7EE7\u5730\u5740\u7F16\u8F91\u5DF2\u79FB\u9664\uFF082026-08 \u5B89\u5168\u6536\u7D27\uFF1A\u9632\u7BE1\u6539\u4E2D\u7EE7\u670D\u52A1\u5668\uFF1B\u6362\u4E2D\u7EE7\u8D70 keys.relayUrl \u6587\u4EF6\u914D\u7F6E\uFF09\n\n const connected = status?.connection === 'connected';\n const devices = status?.devices || [];\n // \u63A8\u8350\u63D2\u4EF6\uFF1A\u53EA\u5C55\u793A\u672A\u5B89\u88C5\u7684\uFF1B\u5168\u90E8\u5DF2\u5B89\u88C5\u5219\u6574\u5361\u9690\u85CF\uFF08\u8FC7\u6EE4\u5DF2\u88C5\u63D2\u4EF6\uFF09\n const recommendedPending = (recommended || []).filter((p) => !p.installed);\n\n return (\n <div style={sectionStyle}>\n {/* \u6807\u9898\u884C\uFF082026-08 \u5BF9\u9F50 DSH \u5185\u7F6E\u8BBE\u7F6E\u9875 .heading\uFF1A18px/600\uFF09+ \u53F3\u4FA7\u7248\u672C\u53F7 + SVG \u68C0\u67E5\u6309\u94AE */}\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>\n <h2 style={{ margin: 0, fontSize: 18, fontWeight: 600, color: 'var(--dsw-alias-label-primary, inherit)' }}>\n {t('nav')}\n </h2>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>\n <code style={{ fontSize: 13, opacity: 0.75 }}>{status?.version ? `v${status.version}` : '\u2026'}</code>\n <button\n title={t('checkUpdate')}\n aria-label={t('checkUpdate')}\n onClick={() => loadUpdate(true)}\n disabled={updateBusy}\n style={{\n border: 'none', background: 'transparent', cursor: updateBusy ? 'default' : 'pointer',\n padding: 6, borderRadius: 6, color: 'inherit', opacity: updateBusy ? 0.4 : 0.7, lineHeight: 0,\n }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" />\n <polyline points=\"21 3 21 9 15 9\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* \u4ECB\u7ECD\uFF08\u5BF9\u9F50 DSH .intro\uFF1A13px + label-tertiary\uFF09 */}\n <p style={{ margin: 0, fontSize: 13, color: 'var(--dsw-alias-label-tertiary, inherit)', lineHeight: 1.6, maxWidth: 560 }}>\n {t('intro')}\n </p>\n\n {/* \u4F9D\u8D56\u81EA\u68C0\u5931\u8D25\uFF082026-09\uFF09\uFF1Alink: \u672C\u5730\u5B89\u88C5 + \u4ED3\u5E93 node_modules \u4E0D\u5B8C\u6574\u4F1A\u8BA9\u63D2\u4EF6\u52A0\u8F7D\u76F4\u63A5\u5D29 dsh web\u3002\n \u8FD9\u91CC\u628A\"\u7F3A\u54EA\u4E2A\u5305 / \u600E\u4E48\u4FEE\"\u76F4\u63A5\u663E\u793A\u51FA\u6765\uFF0C\u907F\u514D\u7528\u6237\u53EA\u770B\u5230\u4E00\u884C ERR_MODULE_NOT_FOUND\u3002 */}\n {doctorInfo && doctorInfo.dependencies && !doctorInfo.dependencies.ok && (\n <div style={{ ...cardStyle, borderColor: '#d93025', padding: '10px 16px' }}>\n <div style={{ fontSize: 13, color: '#d93025', fontWeight: 600 }}>{t('depsBroken')}</div>\n <div style={{ fontSize: 12, lineHeight: 1.6, opacity: 0.85 }}>\n {doctorInfo.dependencies.missing.length\n ? `${t('depsMissing')}${doctorInfo.dependencies.missing.join(', ')}`\n : t('depsBroken')}\n </div>\n <div style={{ fontSize: 12, opacity: 0.7 }}>{t('depsFix')}</div>\n </div>\n )}\n\n {/* \u6709\u65B0\u7248\uFF1A\u6761\u4EF6\u6A2A\u5E45\uFF08\u4EC5\u66F4\u65B0\u53EF\u7528\u65F6\u51FA\u73B0\uFF0C\u4E0D\u5F71\u54CD\u5E38\u89C4\u5E03\u5C40\uFF09 */}\n {updateInfo?.updateAvailable && (\n <div style={{ ...cardStyle, borderColor: '#e8890c', padding: '10px 16px' }}>\n <div style={{ ...rowStyle, gap: 10 }}>\n <span style={{ fontSize: 13, color: '#e8890c' }}>\n {t('updateAvailable')} v{updateInfo.latest}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <button style={btnStyle} onClick={onUpgrade} disabled={updateBusy}>\n {updateBusy ? t('upgrading') : t('upgrade')}\n </button>\n </span>\n </div>\n </div>\n )}\n\n {/* \u672C\u5730\u5F00\u53D1\u7248\u6BD4 npm \u65B0\uFF082026-09\uFF09\uFF1A\u4E0D\u663E\u793A\u5347\u7EA7\u6309\u94AE\uFF0C\u660E\u793A\u539F\u56E0\u2014\u2014\u65E7\u5B9E\u73B0\u5728\u6B64\u5904\u8BEF\u62A5\"\u53D1\u73B0\u65B0\u7248\u672C\"\u5E76\u964D\u7EA7 */}\n {updateInfo?.localAhead && (\n <div style={{ fontSize: 12, color: 'var(--dsw-alias-label-tertiary, inherit)', display: 'flex', alignItems: 'center', gap: 6 }}>\n <span aria-hidden>\u2022</span>\n <span>{t('localAhead')}</span>\n </div>\n )}\n\n {/* \u8FDE\u63A5\u72B6\u6001 */}\n <div style={cardStyle}>\n {/* \u4E2D\u7EE7\u670D\u52A1\u5668\uFF082026-08 \u5B89\u5168\u6536\u7D27\uFF09\uFF1A\u4E0D\u663E\u793A\u5730\u5740\u4E0E\u7F16\u8F91\u6309\u94AE\uFF0C\u53EA\u663E\u793A\u8FDE\u63A5\u72B6\u6001\uFF1B\n \u5730\u5740\u7ECF\u72B6\u6001\u5FBD\u6807\u60AC\u6D6E\u63D0\u793A\uFF08title\uFF09 */}\n <div style={rowStyle}>\n <span style={labelStyle}>{t('relayServer')}</span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n {statusBadge(status?.connection, t, status?.relayUrl)}\n {tokenBadge(status?.tokenState, t)}\n </span>\n </div>\n {/* \u7248\u672C + \u5347\u7EA7\u68C0\u67E5\u5DF2\u79FB\u81F3\u9875\u9762\u6807\u9898\u884C\uFF082026-08 \u5E03\u5C40\u4FEE\u6B63\uFF09\uFF1A\u907F\u514D\u5360\u7528\u8FDE\u63A5\u72B6\u6001\u5361\u8868\u683C\u884C */}\n <div style={rowStyle}>\n <span style={labelStyle}>{t('accountName')}</span>\n <code>{status?.accountName || '\u2026'}</code>\n </div>\n <div style={rowStyle}>\n <span style={labelStyle}>{t('deviceId')}</span>\n <code>{status?.deviceId ? status.deviceId.slice(0, 12) + '\u2026' : '\u2026'}</code>\n </div>\n <div style={rowStyle}>\n <span style={labelStyle}>{t('hostname')}</span>\n <code>{status?.hostname || '\u2026'}</code>\n </div>\n </div>\n\n {/* \u63A8\u8350\u63D2\u4EF6\uFF082026-08\uFF09\uFF1A\u5B98\u65B9\u63D2\u4EF6\u4E00\u952E\u88C5\u5230\u672C\u673A dsh web\uFF08\u767D\u540D\u5355\uFF0C\u70ED\u52A0\u8F7D\uFF09\uFF1B\u53EA\u5C55\u793A\u672A\u5B89\u88C5\u7684\uFF0C\n \u5168\u90E8\u5DF2\u5B89\u88C5\u5219\u6574\u5361\u9690\u85CF\uFF08recommendedPending \u8FC7\u6EE4 installed\uFF09 */}\n {recommendedPending.length > 0 && (\n <div style={cardStyle}>\n <div style={{ fontSize: 15, fontWeight: '600' }}>{t('recommendedTitle')}</div>\n <div style={{ fontSize: 13, opacity: 0.7, lineHeight: 1.5 }}>{t('recommendedHint')}</div>\n {recommendedPending.map((p) => (\n <div key={p.id} style={rowStyle}>\n <span style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>\n <span>{p.icon ? <span style={{ fontSize: 16 }}>\uD83D\uDCE6</span> : null}</span>\n <span><strong>{p.title}</strong> \u2014 {p.description}</span>\n </span>\n <button\n style={btnStyle}\n onClick={() => onInstallPlugin(p.id)}\n disabled={installBusy !== null}\n >\n {installBusy === p.id ? t('installing') : t('install')}\n </button>\n {installNotice?.id === p.id && (\n <span style={{ fontSize: 12, color: installNotice.kind === 'err' ? '#c92a2a' : '#2f9e44' }}>\n {installNotice.text}\n </span>\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* \u914D\u5BF9 */}\n <div style={cardStyle}>\n <div style={rowStyle}>\n <strong>{t('pairingTitle')}</strong>\n {!pairing && (\n <button style={btnStyle} onClick={onStartPairing} disabled={busy !== null || !connected}>\n {t('startPairing')}\n </button>\n )}\n </div>\n\n {pairing && pairing.phase === 'waiting-scan' && pairing.qrDataUrl && (\n <div style={{ display: 'grid', justifyItems: 'center', gap: 10 }}>\n <img src={pairing.qrDataUrl} alt=\"QR\" style={{ width: 220, height: 220, borderRadius: 8 }} />\n <div style={{ fontSize: 13, opacity: 0.75 }}>{t('scanHint')}</div>\n <div style={{ fontSize: 12, opacity: 0.6 }}>\n {pairing.pairingId} \u00B7 {pairing.fp.slice(0, 12)}\u2026\n </div>\n <button style={btnStyle} onClick={onCopyLink}>{t('copyLink')}</button>\n </div>\n )}\n\n {pairing && pairing.phase === 'done' && (\n <div style={{ display: 'grid', gap: 10 }}>\n <div style={{ fontSize: 14, color: '#2f9e44' }}>{t('pairingDone')}</div>\n <button style={btnStyle} onClick={() => setPairing(null)} disabled={busy !== null}>\n {t('startPairing')}\n </button>\n </div>\n )}\n </div>\n\n {/* \u914D\u5BF9\u786E\u8BA4\u5F39\u7A97\uFF1A\u624B\u673A\u63D0\u4EA4\u540E\u5F39\u51FA\uFF0C\u663E\u773C\u7684\u786E\u8BA4/\u62D2\u7EDD\uFF08\u4E0D\u518D\u7528\u4E0D\u663E\u773C\u7684\u5185\u8054\u6309\u94AE\uFF09 */}\n {pairing && pairing.phase === 'awaiting-confirm' && (\n <div style={modalBackdropStyle}>\n <div style={modalCardStyle}>\n <div style={modalTitleStyle}>{t('confirmPairing')}</div>\n <div style={{ fontSize: 14, opacity: 0.8, marginBottom: 6 }}>\n {t('awaitingConfirm')}\n </div>\n <div style={{ fontSize: 13, opacity: 0.65, marginBottom: 14 }}>\n {/* \u53EF\u8BFB\u8BBE\u5907\u540D\u4F18\u5148\uFF1B\u65E0\uFF08\u65E7\u624B\u673A\u672A\u63D0\u4EA4\uFF09\u5219\u56DE\u9000\u8BBE\u5907 ID \u77ED\u7801 */}\n {pairing.phoneName\n ? `${t('deviceId')}: ${pairing.phoneName}`\n : `${pairing.pairingId.slice(0, 8)}\u2026 \u00B7 ${t('deviceId')}: ${pairing.phonePk ? pairing.phonePk.slice(0, 10) + '\u2026' : '\u2014'}`}\n </div>\n {/* A0\uFF08S0\uFF09\u914D\u5BF9\u951A\u5B9A\uFF1A\u9996\u6B21\u914D\u5BF9\u5C55\u793A\u77ED\u7801\uFF0C\u4E0E\u624B\u673A\u5C4F\u5E55\u6BD4\u5BF9\u4E00\u81F4\u624D\u786E\u8BA4\uFF08\u4E2D\u7EE7\u65E0\u6CD5\u4F2A\u9020\u4E24\u7AEF\uFF09 */}\n {pairing.shortCode && (\n <div style={{ background: 'var(--dsh-border, rgba(127,127,127,.08))', borderRadius: 8, padding: '12px 14px', marginBottom: 12, textAlign: 'center' }}>\n <div style={{ fontSize: 12, opacity: 0.7, marginBottom: 6 }}>{t('shortCodeHint')}</div>\n <div style={{ fontSize: 28, fontWeight: 800, letterSpacing: 4, fontFamily: 'monospace', color: 'var(--dsh-fg, #1a1d26)' }}>{pairing.shortCode}</div>\n <div style={{ fontSize: 12, opacity: 0.6, marginTop: 6, lineHeight: 1.5 }}>{t('shortCodeCompare')}</div>\n </div>\n )}\n {pairing.known && (\n <div style={{ fontSize: 13, opacity: 0.7, marginBottom: 12 }}>{t('knownDevice')}</div>\n )}\n {/* \u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF082026-08\uFF09\uFF1A\u786E\u8BA4\u65F6\u9009\u62E9\uFF0C\u914D\u5BF9\u540E\u53EF\u5728\u8BBE\u5907\u5217\u8868\u4FEE\u6539 */}\n <label style={{ ...switchStyle, marginBottom: 16, cursor: 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={confirmFullDisk}\n onChange={(e) => setConfirmFullDisk(e.target.checked)}\n disabled={busy !== null}\n />\n <span>{t('fullDisk')}</span>\n </label>\n <div style={{ fontSize: 12, opacity: 0.6, marginBottom: 16, lineHeight: 1.5 }}>{t('fullDiskHint')}</div>\n <div style={{ display: 'flex', gap: 10 }}>\n <button style={{ ...btnStyle, flex: 1, background: '#fff', borderColor: '#c92a2a', color: '#c92a2a', fontWeight: '600', padding: '12px 16px' }} onClick={onReject} disabled={busy !== null}>\n {t('reject')}\n </button>\n <button style={{ ...btnStyle, flex: 2, background: '#2f9e44', borderColor: '#2f9e44', color: '#fff', fontWeight: '700', padding: '12px 16px' }} onClick={onConfirm} disabled={busy !== null}>\n {t('approve')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* \u5DF2\u914D\u5BF9\u8BBE\u5907 */}\n <div style={cardStyle}>\n <strong>{t('pairedDevices')}</strong>\n {devices.length === 0 && <div style={{ fontSize: 13, opacity: 0.6 }}>{t('noDevices')}</div>}\n {devices.map((d) => (\n <div key={d.phonePk} style={{ ...rowStyle, padding: '6px 0', borderTop: '1px solid var(--dsh-border, rgba(127,127,127,.15))' }}>\n <div style={{ display: 'grid', gap: 4 }}>\n {/* \u8BBE\u5907\u540D\uFF08\u624B\u673A\u4E0A\u62A5\uFF0C2026-08\uFF09\uFF1A\u6709\u540D\u5B57\u663E\u793A\u540D\u5B57\uFF0C\u5426\u5219\u56DE\u9000\u77ED\u7801 */}\n <code style={{ fontSize: 13, fontWeight: d.name ? 600 : 400 }}>{d.name || d.shortId + '\u2026'}</code>\n <span style={{ fontSize: 12, opacity: 0.6 }}>\n {t('lastUsed')}: {fmtTime(d.lastUsedAt, t)}\n </span>\n {/* \u5B8C\u6574\u78C1\u76D8\u6743\u9650\uFF1A\u663E\u793A + \u4FEE\u6539\uFF082026-08\uFF09 */}\n <label style={{ ...switchStyle, cursor: 'pointer', opacity: 0.9 }}>\n <input\n type=\"checkbox\"\n checked={!!(d.permission && d.permission.fullDisk)}\n onChange={(e) => onTogglePermission(d.phonePk, e.target.checked)}\n disabled={busy !== null}\n />\n <span>{t('fullDisk')}</span>\n </label>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>\n {/* \u8FDE\u63A5\u72B6\u6001\u63D0\u793A\uFF08M5.12\uFF09\uFF1A\u5728\u7EBF\u7EFF / \u79BB\u7EBF\u7070\uFF0C\u4F4D\u4E8E\u89E3\u9664\u914D\u5BF9\u6309\u94AE\u5DE6\u4FA7 */}\n <span style={deviceStatusStyle(d.online !== false)}>\n {d.online === false ? `\u25CB ${t('deviceOffline')}` : `\u25CF ${t('deviceOnline')}`}\n </span>\n <button style={btnStyle} onClick={() => onUnpair(d.phonePk)} disabled={busy !== null}>\n {t('unpair')}\n </button>\n </div>\n </div>\n ))}\n </div>\n\n {notice && <div style={{ fontSize: 13, color: '#2f9e44' }}>{notice}</div>}\n {error && <div style={{ fontSize: 13, color: '#c92a2a' }}>{error}</div>}\n\n {/* \u5F31\u5F39\u7A97\uFF082026-08\uFF1A\u68C0\u67E5\u66F4\u65B0\u5931\u8D25/\u5DF2\u6700\u65B0\u3001\u5347\u7EA7\u7ED3\u679C\uFF1Bfixed \u53F3\u4E0A\u89D2\uFF0C3s \u81EA\u52A8\u6D88\u5931\uFF0C\u4E0D\u5E38\u9A7B\uFF09 */}\n {toast && (\n <div style={{\n position: 'fixed', top: 24, right: 24, zIndex: 10000,\n background: toast.kind === 'err' ? '#c92a2a' : '#2f9e44',\n color: '#fff', borderRadius: 8, padding: '10px 16px', fontSize: 13,\n boxShadow: '0 8px 24px rgba(0,0,0,.25)', maxWidth: 320, lineHeight: 1.5,\n }}>\n {toast.text}\n </div>\n )}\n </div>\n );\n}\n", "// packages/remote-connector/src/client/api.js\n// /dshfly \u540E\u7AEF\u8DEF\u7531\u7684\u524D\u7AEF\u5C01\u88C5\uFF08\u540C\u6E90\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u6D4F\u89C8\u5668 fetch\uFF09\u3002\n// \u8DEF\u7531\u5B9E\u73B0\u89C1 ../../http-api.js\uFF1Bloopback \u6805\u680F\u8981\u6C42 Host \u4E3A 127.0.0.1\uFF08dsh web \u6052\u7ED1 loopback\uFF0C\n// \u6D4F\u89C8\u5668\u540C\u6E90\u8BF7\u6C42\u5929\u7136\u6EE1\u8DB3\uFF1BPOST \u5E26\u540C\u6E90 Origin \u4EA6\u901A\u8FC7\uFF09\u3002\n\nasync function request(path, init) {\n const headers = new Headers(init?.headers);\n if (init?.body !== undefined && !headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n const response = await fetch(path, { ...init, headers, cache: 'no-store' });\n let payload = null;\n try {\n payload = await response.json();\n } catch {}\n if (!response.ok) {\n throw new Error((payload && payload.error) || `HTTP ${response.status}`);\n }\n return payload;\n}\n\nexport const getStatus = () => request('/dshfly/status');\nexport const startPairing = () => request('/dshfly/pair', { method: 'POST' });\nexport const getPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}`);\nexport const confirmPairing = (id, fullDisk = false) =>\n request(`/dshfly/pair/${encodeURIComponent(id)}/confirm`, { method: 'POST', body: JSON.stringify({ fullDisk }) });\nexport const rejectPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}/reject`, { method: 'POST' });\nexport const unpair = (phonePk) => request('/dshfly/unpair', { method: 'POST', body: JSON.stringify({ phonePk }) });\nexport const setPermission = (phonePk, fullDisk) =>\n request('/dshfly/permissions', { method: 'POST', body: JSON.stringify({ phonePk, fullDisk }) });\nexport const getRecommendedPlugins = () => request('/dshfly/plugins/recommended');\nexport const installPlugin = (id) =>\n request('/dshfly/plugins/install', { method: 'POST', body: JSON.stringify({ id }) });\nexport const checkUpdate = () => request('/dshfly/plugins/checkUpdate');\nexport const upgrade = () => request('/dshfly/plugins/upgrade', { method: 'POST' });\nexport const doctor = () => request('/dshfly/doctor');\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["React"]
|
|
7
7
|
}
|
package/http-api.js
CHANGED
|
@@ -8,6 +8,8 @@ import { execFile as defaultExecFile } from 'node:child_process';
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
|
+
import { summarizeUpdate } from './core/version.js';
|
|
12
|
+
import { checkDependencies, dependencyFixHint } from './core/deps-check.js';
|
|
11
13
|
|
|
12
14
|
const require = createRequire(import.meta.url);
|
|
13
15
|
|
|
@@ -93,6 +95,7 @@ export function createDshflyHttpRoute(core, opts = {}) {
|
|
|
93
95
|
execFile = defaultExecFile, // 测试注入
|
|
94
96
|
version = '', // 本插件本地版本(package.json)
|
|
95
97
|
registryFetch = fetch, // registry 查询用(测试注入)
|
|
98
|
+
depsCheck = checkDependencies, // 依赖预检(测试注入)
|
|
96
99
|
} = opts;
|
|
97
100
|
const handler = async (req, res) => {
|
|
98
101
|
if (!isTrustedLoopbackRequest(req)) {
|
|
@@ -233,6 +236,8 @@ export function createDshflyHttpRoute(core, opts = {}) {
|
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
// 检查本插件更新(2026-08):npm registry 最新版 vs 本地版本(失败 → latest=null,不阻塞)
|
|
239
|
+
// 2026-09 修复:原先 `latest !== version` 只比相等不比大小 → 本地开发版(仓库 0.3.0)
|
|
240
|
+
// 在 npm 最新版(0.2.1)更旧时仍报"发现新版本",点击即降级。改按 semver 顺序判定。
|
|
236
241
|
if (m === 'GET' && p === '/dshfly/plugins/checkUpdate') {
|
|
237
242
|
let latest = null;
|
|
238
243
|
let error = null;
|
|
@@ -248,25 +253,92 @@ export function createDshflyHttpRoute(core, opts = {}) {
|
|
|
248
253
|
} catch (e) {
|
|
249
254
|
error = e?.message || 'registry 查询失败';
|
|
250
255
|
}
|
|
256
|
+
const { updateAvailable, localAhead, comparable } = summarizeUpdate({ current: version, latest });
|
|
251
257
|
return send(res, 200, {
|
|
252
258
|
current: version,
|
|
253
259
|
latest,
|
|
254
|
-
updateAvailable
|
|
260
|
+
updateAvailable,
|
|
261
|
+
// 本地(开发/预发布)版本比 registry 更新:UI 明示,避免用户误以为检查坏了
|
|
262
|
+
localAhead,
|
|
263
|
+
comparable,
|
|
255
264
|
error,
|
|
256
265
|
});
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
// 一键升级本插件(2026-08):dsh plugin add <pkg>@latest + 提示重启(升级不能热加载——替换已加载插件需清模块缓存)
|
|
269
|
+
// 2026-09 修复:先查 registry 再决定——**绝不把更新的本地版本降级成 npm 旧版**。
|
|
270
|
+
// `dry=1`(或 body.dryRun=true)只做预检不安装,供测试与 UI 预检使用。
|
|
260
271
|
if (m === 'POST' && p === '/dshfly/plugins/upgrade') {
|
|
272
|
+
const body = await readJson(req).catch(() => ({}));
|
|
273
|
+
const dryRun = url.searchParams.get('dry') === '1' || body?.dryRun === true;
|
|
274
|
+
const pkgName = require('./package.json').name;
|
|
275
|
+
let latest = null;
|
|
276
|
+
let error = null;
|
|
277
|
+
try {
|
|
278
|
+
const r = await registryFetch(`https://registry.npmjs.org/${encodeURIComponent(pkgName)}/latest`, { signal: AbortSignal.timeout(8000) });
|
|
279
|
+
if (r.ok) {
|
|
280
|
+
const j = await r.json();
|
|
281
|
+
latest = typeof j?.version === 'string' ? j.version : null;
|
|
282
|
+
} else {
|
|
283
|
+
error = `registry HTTP ${r.status}`;
|
|
284
|
+
}
|
|
285
|
+
} catch (e) {
|
|
286
|
+
error = e?.message || 'registry 查询失败';
|
|
287
|
+
}
|
|
288
|
+
const { updateAvailable, localAhead, comparable } = summarizeUpdate({ current: version, latest });
|
|
289
|
+
if (error || !latest) {
|
|
290
|
+
return send(res, 502, {
|
|
291
|
+
error: `无法查询 npm registry 版本,未执行升级:${error || '未取到版本号'}`,
|
|
292
|
+
current: version,
|
|
293
|
+
latest,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (!comparable) {
|
|
297
|
+
return send(res, 409, {
|
|
298
|
+
error: `无法比较版本号(本地 ${version} / registry ${latest}),未执行升级`,
|
|
299
|
+
current: version,
|
|
300
|
+
latest,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
if (!updateAvailable) {
|
|
304
|
+
return send(res, 409, {
|
|
305
|
+
error: localAhead
|
|
306
|
+
? `本地版本 ${version} 比 npm 上的 ${latest} 更新(开发/预发布版本),已阻止降级;如需强制安装 npm 版请用 dsh plugin add ${pkgName}@latest`
|
|
307
|
+
: `已是最新版本(${version}),无需升级`,
|
|
308
|
+
code: localAhead ? 'LOCAL_AHEAD' : 'UP_TO_DATE',
|
|
309
|
+
current: version,
|
|
310
|
+
latest,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (dryRun) {
|
|
314
|
+
return send(res, 200, { ok: true, dryRun: true, package: pkgName, current: version, latest, wouldUpgrade: true });
|
|
315
|
+
}
|
|
261
316
|
try {
|
|
262
|
-
const pkgName = require('./package.json').name;
|
|
263
317
|
await runDshPluginAdd(dshBin, profileName, `${pkgName}@latest`, execFile);
|
|
264
|
-
return send(res, 200, { ok: true, package: pkgName, rebootNeeded: true });
|
|
318
|
+
return send(res, 200, { ok: true, package: pkgName, from: version, to: latest, rebootNeeded: true });
|
|
265
319
|
} catch (e) {
|
|
266
320
|
return send(res, 500, { error: `升级失败:${e.message}` });
|
|
267
321
|
}
|
|
268
322
|
}
|
|
269
323
|
|
|
324
|
+
// 自检(2026-09):依赖可解析性——真机事故是 "dsh plugin add ."(link: 安装)后仓库
|
|
325
|
+
// node_modules 不完整 → 插件 apply 抛 ERR_MODULE_NOT_FOUND → fail-loud 拖垮整个 dsh web。
|
|
326
|
+
// 该端点只读、loopback 栅栏内,设置页据此显示"缺依赖 + 修复命令"。
|
|
327
|
+
if (m === 'GET' && p === '/dshfly/doctor') {
|
|
328
|
+
const deps = depsCheck();
|
|
329
|
+
return send(res, 200, {
|
|
330
|
+
version,
|
|
331
|
+
dshVersion: resolveDshVersion(dshBin),
|
|
332
|
+
dependencies: {
|
|
333
|
+
ok: deps.ok,
|
|
334
|
+
missing: deps.missing,
|
|
335
|
+
hostOptional: deps.hostOptional,
|
|
336
|
+
checked: deps.checked.length,
|
|
337
|
+
hint: deps.ok ? null : dependencyFixHint(deps.missing),
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
270
342
|
return send(res, 404, { error: 'not found' });
|
|
271
343
|
} catch (e) {
|
|
272
344
|
return fail(res, e.status || 500, e);
|
package/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createRequire } from 'node:module';
|
|
|
16
16
|
import { ConnectorCore } from './core/connector-core.js';
|
|
17
17
|
import { createDshflyHttpRoute, installRecommendedPlugin, uninstallPlugin, resolveDshVersion, resolveDshNodeModules } from './http-api.js';
|
|
18
18
|
import { resolveConfig, validateConfig, Config } from './config.js';
|
|
19
|
-
import { MobileBridgeCore, createMobileBridgeService, resolveProfileDir } from './mobile-bridge/index.js';
|
|
19
|
+
import { MobileBridgeCore, createMobileBridgeService, resolveProfileDir, GitStatusService } from './mobile-bridge/index.js';
|
|
20
20
|
import { createDshAdapter } from './core/dsh-web.js';
|
|
21
21
|
|
|
22
22
|
const require = createRequire(import.meta.url);
|
|
@@ -85,9 +85,13 @@ export async function apply(ctx, config = {}) {
|
|
|
85
85
|
return [];
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
+
const gitStatusSvc = new GitStatusService();
|
|
88
89
|
const bridgeCore = new MobileBridgeCore({
|
|
89
90
|
profileDir: resolveProfileDir(cfg),
|
|
90
91
|
resolveRoots,
|
|
92
|
+
// 2026-09:文件级 Git 状态注记(files.list 给文件加 gitStatus → 手机端彩色小点)。
|
|
93
|
+
// 只读 git status,非 git 仓库/git 缺失 → 空表无点;复用 4s TTL 缓存,下拉刷新/进目录触发。
|
|
94
|
+
gitStatusProvider: (dir) => gitStatusSvc.statusForDir(dir),
|
|
91
95
|
// 2026-09:dsh.profile.bundles 里混有 DSH 内置运行时 bundle(@deepseek-ai/dsh-base 等),
|
|
92
96
|
// 它们在 DSH 应用 node_modules、不在 profile node_modules → 传入 DSH 应用 node_modules 作为
|
|
93
97
|
// 额外解析根,避免把 DSH 内置包误报为"bundle 目录解析失败"。
|
|
@@ -41,20 +41,22 @@ function resolvePluginString(value, lang) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export class MobileBridgeCore {
|
|
44
|
-
/** @param {{profileDir?: string|null, resolveRoots?: () => Promise<string[]>, bundleResolveRoots?: string[]}} opts
|
|
44
|
+
/** @param {{profileDir?: string|null, resolveRoots?: () => Promise<string[]>, bundleResolveRoots?: string[], gitStatusProvider?: (absDir: string) => Promise<Map<string, string>>}} opts
|
|
45
45
|
* profileDir=null 时只支持动态注册(单测/无 DSH 宿主场景)。
|
|
46
46
|
* resolveRoots:文件树服务(mobile.files.*,方案 docs/file-tree-plan.md)的根白名单提供者,
|
|
47
47
|
* 由宿主(remote-connector)注入;未注入时 files.* RPC 返回 INTERNAL。
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* gitStatusProvider:文件级 Git 状态注记(docs/…,2026-09);传入 => FilesService.list
|
|
49
|
+
* 会给文件条目加 gitStatus(modified/added/untracked/deleted/conflict 等),手机端渲染彩色小点;
|
|
50
|
+
* 未注入/失败 → 无点。bundleResolveRoots:额外 bundle 解析根(如 DSH 应用自身的 node_modules)。
|
|
51
|
+
* DSH 的 dsh.profile.bundles 里混有"DSH 内置运行时 bundle"(@deepseek-ai/dsh-base 等,在 DSH 应用
|
|
50
52
|
* node_modules,不在 profile node_modules)——枚举时先查 profile node_modules,找不到再查这些
|
|
51
53
|
* 附加根,避免把 DSH 内置包误报为"bundle 目录解析失败"。 */
|
|
52
|
-
constructor({ profileDir = null, resolveRoots = null, bundleResolveRoots = [] } = {}) {
|
|
54
|
+
constructor({ profileDir = null, resolveRoots = null, bundleResolveRoots = [], gitStatusProvider = null } = {}) {
|
|
53
55
|
this.profileDir = profileDir;
|
|
54
56
|
/** 额外 bundle 解析根(DSH 应用 node_modules 等)。 */
|
|
55
57
|
this.bundleResolveRoots = Array.isArray(bundleResolveRoots) ? bundleResolveRoots.filter((d) => typeof d === 'string' && d) : [];
|
|
56
58
|
/** 文件树只读服务(mobile.files.list/read)。 */
|
|
57
|
-
this.files = resolveRoots ? new FilesService({ resolveRoots }) : null;
|
|
59
|
+
this.files = resolveRoots ? new FilesService({ resolveRoots, gitStatusProvider }) : null;
|
|
58
60
|
/** @type {Map<string, {manifest: object, staticSource: string|null, handler: object|null, registeredAt: number|null}>} */
|
|
59
61
|
this.entries = new Map();
|
|
60
62
|
/** @type {Array<{pluginId?: string, message: string}>} PC 设置页健康报告。 */
|
|
@@ -67,8 +67,11 @@ export class FilesService {
|
|
|
67
67
|
* @param {{resolveRoots?: () => Promise<string[]>, pageSize?: number, pageBytes?: number, maxPreviewBytes?: number}} opts
|
|
68
68
|
* resolveRoots:每次调用实时返回允许的根(canonical 绝对路径)集合;未注入时 files.* 停用。
|
|
69
69
|
*/
|
|
70
|
-
constructor({ resolveRoots = null, pageSize = FILES_DEFAULTS.pageSize, pageBytes = FILES_DEFAULTS.pageBytes, maxPreviewBytes = FILES_DEFAULTS.maxPreviewBytes, maxDownloadBytes = FILES_DEFAULTS.maxDownloadBytes, defaultChunkBytes = FILES_DEFAULTS.defaultChunkBytes } = {}) {
|
|
70
|
+
constructor({ resolveRoots = null, gitStatusProvider = null, pageSize = FILES_DEFAULTS.pageSize, pageBytes = FILES_DEFAULTS.pageBytes, maxPreviewBytes = FILES_DEFAULTS.maxPreviewBytes, maxDownloadBytes = FILES_DEFAULTS.maxDownloadBytes, defaultChunkBytes = FILES_DEFAULTS.defaultChunkBytes } = {}) {
|
|
71
71
|
this.resolveRoots = resolveRoots;
|
|
72
|
+
// git 状态注记 provider(方案 2026-09):(absDir) => Promise<Map<absPath, category>>。
|
|
73
|
+
// 未注入 → 列表条目不带 gitStatus(手机端不显示点);provider 抛错/非 Map → 静默无点。
|
|
74
|
+
this.gitStatusProvider = typeof gitStatusProvider === 'function' ? gitStatusProvider : null;
|
|
72
75
|
this.pageSize = pageSize;
|
|
73
76
|
this.pageBytes = pageBytes;
|
|
74
77
|
this.maxPreviewBytes = maxPreviewBytes;
|
|
@@ -226,6 +229,24 @@ export class FilesService {
|
|
|
226
229
|
const start = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
|
|
227
230
|
const page = rows.slice(start, start + this.pageSize);
|
|
228
231
|
const truncated = start + page.length < rows.length;
|
|
232
|
+
// Git 状态注记(文件级:仅文件上点;目录不显示)。provider 未注入/git 判定失败 → 无点(不抛错)。
|
|
233
|
+
if (this.gitStatusProvider) {
|
|
234
|
+
let statusMap = new Map();
|
|
235
|
+
try {
|
|
236
|
+
const m = await this.gitStatusProvider(target);
|
|
237
|
+
if (m instanceof Map) statusMap = m;
|
|
238
|
+
} catch {
|
|
239
|
+
// git 探测失败:静默无点,绝不影响 files.list 主链路
|
|
240
|
+
}
|
|
241
|
+
if (statusMap.size) {
|
|
242
|
+
for (const e of page) {
|
|
243
|
+
if (e.type === 'file') {
|
|
244
|
+
const cat = statusMap.get(e.path);
|
|
245
|
+
if (cat) e.gitStatus = cat;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
229
250
|
return {
|
|
230
251
|
root: target,
|
|
231
252
|
path: target,
|