@artenx/dshb 0.0.3 → 0.1.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.
Files changed (166) hide show
  1. package/cordis.patch.yml +8 -49
  2. package/lib/auth/credentials.d.ts +30 -0
  3. package/lib/auth/credentials.js +121 -0
  4. package/lib/auth/credentials.js.map +1 -0
  5. package/lib/auth/decorate-webserver.d.ts +7 -0
  6. package/lib/auth/decorate-webserver.js +75 -0
  7. package/lib/auth/decorate-webserver.js.map +1 -0
  8. package/lib/auth/gate.d.ts +14 -0
  9. package/lib/auth/gate.js +45 -0
  10. package/lib/auth/gate.js.map +1 -0
  11. package/lib/auth/index.d.ts +8 -0
  12. package/lib/auth/index.js +51 -0
  13. package/lib/auth/index.js.map +1 -0
  14. package/lib/auth/login-page.d.ts +5 -0
  15. package/lib/auth/login-page.js +87 -0
  16. package/lib/auth/login-page.js.map +1 -0
  17. package/lib/auth/loopback-compat.d.ts +6 -0
  18. package/lib/auth/loopback-compat.js +94 -0
  19. package/lib/auth/loopback-compat.js.map +1 -0
  20. package/lib/auth/loopback.d.ts +7 -0
  21. package/lib/auth/loopback.js +24 -0
  22. package/lib/auth/loopback.js.map +1 -0
  23. package/lib/auth/ratelimit.d.ts +11 -0
  24. package/lib/auth/ratelimit.js +37 -0
  25. package/lib/auth/ratelimit.js.map +1 -0
  26. package/lib/auth/routes.d.ts +8 -0
  27. package/lib/auth/routes.js +224 -0
  28. package/lib/auth/routes.js.map +1 -0
  29. package/lib/auth/session.d.ts +11 -0
  30. package/lib/auth/session.js +52 -0
  31. package/lib/auth/session.js.map +1 -0
  32. package/lib/auth/startup.d.ts +15 -0
  33. package/lib/auth/startup.js +68 -0
  34. package/lib/auth/startup.js.map +1 -0
  35. package/lib/auth/webserver.d.ts +18 -0
  36. package/lib/auth/webserver.js +60 -0
  37. package/lib/auth/webserver.js.map +1 -0
  38. package/lib/client.js +2770 -0
  39. package/lib/client.js.map +1 -0
  40. package/lib/connection-sIX25pkS.d.ts +46 -0
  41. package/lib/core/audit.d.ts +16 -0
  42. package/lib/core/audit.js +38 -0
  43. package/lib/core/audit.js.map +1 -0
  44. package/lib/core/index.d.ts +8 -0
  45. package/lib/core/index.js +77 -0
  46. package/lib/core/index.js.map +1 -0
  47. package/lib/core/known-hosts.d.ts +23 -0
  48. package/lib/core/known-hosts.js +76 -0
  49. package/lib/core/known-hosts.js.map +1 -0
  50. package/lib/core/node-registry.d.ts +108 -0
  51. package/lib/core/node-registry.js +191 -0
  52. package/lib/core/node-registry.js.map +1 -0
  53. package/lib/core/routes.d.ts +9 -0
  54. package/lib/core/routes.js +404 -0
  55. package/lib/core/routes.js.map +1 -0
  56. package/lib/core/ssh-config.d.ts +23 -0
  57. package/lib/core/ssh-config.js +70 -0
  58. package/lib/core/ssh-config.js.map +1 -0
  59. package/lib/core/test.d.ts +43 -0
  60. package/lib/core/test.js +116 -0
  61. package/lib/core/test.js.map +1 -0
  62. package/lib/core/workspace-bindings.d.ts +28 -0
  63. package/lib/core/workspace-bindings.js +86 -0
  64. package/lib/core/workspace-bindings.js.map +1 -0
  65. package/lib/core/workspaces.d.ts +9 -0
  66. package/lib/core/workspaces.js +283 -0
  67. package/lib/core/workspaces.js.map +1 -0
  68. package/lib/exec-docker/daemon-runner.d.ts +21 -0
  69. package/lib/exec-docker/daemon-runner.js +326 -0
  70. package/lib/exec-docker/daemon-runner.js.map +1 -0
  71. package/lib/exec-docker/docker-backend.d.ts +48 -0
  72. package/lib/exec-docker/docker-backend.js +1 -0
  73. package/lib/exec-docker/docker-client.d.ts +46 -0
  74. package/lib/exec-docker/docker-client.js +302 -0
  75. package/lib/exec-docker/docker-client.js.map +1 -0
  76. package/lib/exec-docker/docker-routes.d.ts +6 -0
  77. package/lib/exec-docker/docker-routes.js +215 -0
  78. package/lib/exec-docker/docker-routes.js.map +1 -0
  79. package/lib/exec-docker/fs-provider.d.ts +50 -0
  80. package/lib/exec-docker/fs-provider.js +92 -0
  81. package/lib/exec-docker/fs-provider.js.map +1 -0
  82. package/lib/exec-docker/host-runner.d.ts +29 -0
  83. package/lib/exec-docker/host-runner.js +17 -0
  84. package/lib/exec-docker/host-runner.js.map +1 -0
  85. package/lib/exec-docker/index.d.ts +21 -0
  86. package/lib/exec-docker/index.js +137 -0
  87. package/lib/exec-docker/index.js.map +1 -0
  88. package/lib/exec-docker/process-provider.d.ts +20 -0
  89. package/lib/exec-docker/process-provider.js +75 -0
  90. package/lib/exec-docker/process-provider.js.map +1 -0
  91. package/lib/exec-docker/provision.d.ts +24 -0
  92. package/lib/exec-docker/provision.js +103 -0
  93. package/lib/exec-docker/provision.js.map +1 -0
  94. package/lib/exec-docker/remote-docker-cli.d.ts +52 -0
  95. package/lib/exec-docker/remote-docker-cli.js +155 -0
  96. package/lib/exec-docker/remote-docker-cli.js.map +1 -0
  97. package/lib/exec-docker/world.d.ts +37 -0
  98. package/lib/exec-docker/world.js +62 -0
  99. package/lib/exec-docker/world.js.map +1 -0
  100. package/lib/exec-ssh/connection-pool.d.ts +25 -0
  101. package/lib/exec-ssh/connection-pool.js +54 -0
  102. package/lib/exec-ssh/connection-pool.js.map +1 -0
  103. package/lib/exec-ssh/connection.d.ts +2 -0
  104. package/lib/exec-ssh/connection.js +125 -0
  105. package/lib/exec-ssh/connection.js.map +1 -0
  106. package/lib/exec-ssh/env-scrub.d.ts +7 -0
  107. package/lib/exec-ssh/env-scrub.js +40 -0
  108. package/lib/exec-ssh/env-scrub.js.map +1 -0
  109. package/lib/exec-ssh/executor.d.ts +46 -0
  110. package/lib/exec-ssh/executor.js +212 -0
  111. package/lib/exec-ssh/executor.js.map +1 -0
  112. package/lib/exec-ssh/fs-provider.d.ts +50 -0
  113. package/lib/exec-ssh/fs-provider.js +92 -0
  114. package/lib/exec-ssh/fs-provider.js.map +1 -0
  115. package/lib/exec-ssh/index.d.ts +16 -0
  116. package/lib/exec-ssh/index.js +30 -0
  117. package/lib/exec-ssh/index.js.map +1 -0
  118. package/lib/exec-ssh/process-provider.d.ts +20 -0
  119. package/lib/exec-ssh/process-provider.js +75 -0
  120. package/lib/exec-ssh/process-provider.js.map +1 -0
  121. package/lib/exec-ssh/runner.d.ts +13 -0
  122. package/lib/exec-ssh/runner.js +62 -0
  123. package/lib/exec-ssh/runner.js.map +1 -0
  124. package/lib/exec-ssh/world.d.ts +79 -0
  125. package/lib/exec-ssh/world.js +95 -0
  126. package/lib/exec-ssh/world.js.map +1 -0
  127. package/lib/index.d.ts +2 -0
  128. package/lib/index.js +2 -0
  129. package/lib/router/decorate-fs.d.ts +6 -0
  130. package/lib/router/decorate-fs.js +78 -0
  131. package/lib/router/decorate-fs.js.map +1 -0
  132. package/lib/router/decorate-shell.d.ts +6 -0
  133. package/lib/router/decorate-shell.js +35 -0
  134. package/lib/router/decorate-shell.js.map +1 -0
  135. package/lib/router/decorate-subprocess.d.ts +6 -0
  136. package/lib/router/decorate-subprocess.js +35 -0
  137. package/lib/router/decorate-subprocess.js.map +1 -0
  138. package/lib/router/index.d.ts +10 -0
  139. package/lib/router/index.js +21 -0
  140. package/lib/router/index.js.map +1 -0
  141. package/lib/router/resolve.d.ts +22 -0
  142. package/lib/router/resolve.js +45 -0
  143. package/lib/router/resolve.js.map +1 -0
  144. package/lib/router/router-fs.d.ts +25 -0
  145. package/lib/router/router-fs.js +64 -0
  146. package/lib/router/router-fs.js.map +1 -0
  147. package/lib/router/router-shell.d.ts +14 -0
  148. package/lib/router/router-shell.js +36 -0
  149. package/lib/router/router-shell.js.map +1 -0
  150. package/lib/router/router-subprocess.d.ts +13 -0
  151. package/lib/router/router-subprocess.js +33 -0
  152. package/lib/router/router-subprocess.js.map +1 -0
  153. package/lib/router/types.d.ts +2 -0
  154. package/lib/router/types.js +1 -0
  155. package/lib/types-C0TtEO7z.d.ts +67 -0
  156. package/lib/ui/decorate-picker.d.ts +6 -0
  157. package/lib/ui/decorate-picker.js +58 -0
  158. package/lib/ui/decorate-picker.js.map +1 -0
  159. package/lib/ui/index.d.ts +8 -0
  160. package/lib/ui/index.js +11 -0
  161. package/lib/ui/index.js.map +1 -0
  162. package/lib/ui/picker.d.ts +10 -0
  163. package/lib/ui/picker.js +58 -0
  164. package/lib/ui/picker.js.map +1 -0
  165. package/package.json +158 -9
  166. package/LICENSE +0 -21
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["SECTION_ID","useState","useCallback","apply","inputStyle","buttonStyle","primaryButtonStyle","secondaryButtonStyle","useState","useCallback","useState","useCallback","apply"],"sources":["../src/client/auth.tsx","../src/client/directory-flow.tsx","../src/client/mobile.tsx","../src/client/ui.tsx","../src/client/index.tsx"],"sourcesContent":["import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\n\nexport const inject = ['connection']\n\nconst SECTION_ID = 'auth'\nconst MESSAGE_MS = 5000\n\nfunction useUsername(): [string | undefined, (username: string) => void] {\n const [username, setUsername] = useState<string | undefined>(undefined)\n useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await fetch('/api/auth/status')\n if (!res.ok) return\n const data = (await res.json()) as { username?: string }\n if (!cancelled && typeof data.username === 'string') setUsername(data.username)\n } catch {\n // best-effort\n }\n })()\n return () => {\n cancelled = true\n }\n }, [])\n return [username, setUsername]\n}\n\ntype Notice = { kind: 'ok' | 'error'; text: string; owner: 'username' | 'password' | 'account' }\n\nexport function AuthSection(_props: PropsRuntime<'settings.section'>): ReactElement {\n const [username, setUsername] = useUsername()\n const [newUsername, setNewUsername] = useState('')\n const [usernamePassword, setUsernamePassword] = useState('')\n const [oldPassword, setOldPassword] = useState('')\n const [newPassword, setNewPassword] = useState('')\n const [confirm, setConfirm] = useState('')\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<Notice | undefined>(undefined)\n const [confirmingSignOut, setConfirmingSignOut] = useState(false)\n\n const flash = useCallback((n: Notice | undefined) => {\n setNotice(n)\n if (n !== undefined) setTimeout(() => setNotice(undefined), MESSAGE_MS)\n }, [])\n\n const signOut = useCallback(async () => {\n setBusy(true)\n try {\n await fetch('/api/auth/logout', { method: 'POST' })\n window.location.href = '/login'\n } catch {\n setBusy(false)\n setConfirmingSignOut(false)\n flash({ kind: 'error', text: '退出失败,请重试', owner: 'account' })\n }\n }, [flash])\n\n const changePassword = useCallback(async () => {\n if (newPassword !== confirm) {\n flash({ kind: 'error', text: '两次输入的新密码不一致', owner: 'password' })\n return\n }\n setBusy(true)\n try {\n const res = await fetch('/api/auth/change-password', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ currentPassword: oldPassword, password: newPassword }),\n })\n const data = (await res.json()) as { error?: string }\n if (res.ok) {\n setOldPassword('')\n setNewPassword('')\n setConfirm('')\n flash({ kind: 'ok', text: '密码已修改', owner: 'password' })\n } else {\n flash({ kind: 'error', text: data.error ?? '修改失败,请重试', owner: 'password' })\n }\n } catch {\n flash({ kind: 'error', text: '修改失败,请重试', owner: 'password' })\n } finally {\n setBusy(false)\n }\n }, [oldPassword, newPassword, confirm, flash])\n\n const changeUsername = useCallback(async () => {\n setBusy(true)\n try {\n const res = await fetch('/api/auth/change-username', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ username: newUsername, currentPassword: usernamePassword }),\n })\n const data = (await res.json()) as { error?: string }\n if (res.ok) {\n setNewUsername('')\n setUsernamePassword('')\n if (newUsername) setUsername(newUsername)\n flash({ kind: 'ok', text: '用户名已更新', owner: 'username' })\n } else {\n flash({ kind: 'error', text: data.error ?? '修改失败,请重试', owner: 'username' })\n }\n } catch {\n flash({ kind: 'error', text: '修改失败,请重试', owner: 'username' })\n } finally {\n setBusy(false)\n }\n }, [newUsername, usernamePassword, flash, setUsername])\n\n const inputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n padding: '0 10px',\n height: 32,\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: 8,\n fontSize: 14,\n fontFamily: 'inherit',\n background: 'var(--dsw-alias-bg-layer-1)',\n color: 'var(--dsw-alias-label-primary)',\n outline: 'none',\n }\n const buttonStyle: CSSProperties = {\n boxSizing: 'border-box',\n height: 36,\n padding: '0 14px',\n borderRadius: 18,\n fontSize: 14,\n lineHeight: '22px',\n fontFamily: 'inherit',\n cursor: 'pointer',\n border: 'none',\n display: 'inline-flex',\n justifyContent: 'center',\n alignItems: 'center',\n gap: 4,\n }\n const primaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n background: 'var(--dsw-alias-button-primary-fill)',\n color: 'var(--dsw-alias-label-primary-foreground)',\n }\n const secondaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-primary)',\n background: 'transparent',\n }\n const dangerButtonStyle: CSSProperties = {\n ...buttonStyle,\n color: 'var(--dsw-alias-state-error-primary)',\n background: 'transparent',\n }\n const dangerOutlineButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-state-error-primary)',\n color: 'var(--dsw-alias-state-error-primary)',\n background: 'transparent',\n }\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 24, maxWidth: 420 }}>\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 4px' }}>账号</h2>\n <p style={{ fontSize: 13, opacity: 0.7, margin: '0 0 12px' }}>\n {username !== undefined ? `当前登录:${username}` : '当前登录:管理员'}\n </p>\n <button\n type=\"button\"\n onClick={() => setConfirmingSignOut(true)}\n disabled={busy}\n style={dangerOutlineButtonStyle}\n >\n 退出登录\n </button>\n {confirmingSignOut && (\n <div role=\"alertdialog\" aria-label=\"确认退出登录\" style={{ marginTop: 10, padding: '12px 14px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, maxWidth: 320 }}>\n <div style={{ fontSize: 13, marginBottom: 10, color: 'var(--dsw-alias-label-primary)' }}>退出登录将回到登录页</div>\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <button type=\"button\" onClick={() => setConfirmingSignOut(false)} disabled={busy} style={{ ...secondaryButtonStyle, height: 32, padding: '0 12px', borderRadius: 16 }}>\n 取消\n </button>\n <button type=\"button\" onClick={() => void signOut()} disabled={busy} style={{ ...dangerOutlineButtonStyle, height: 32, padding: '0 12px', borderRadius: 16, background: 'var(--dsw-alias-interactive-bg-hover-danger)' }}>\n 确认退出\n </button>\n </div>\n </div>\n )}\n {notice?.owner === 'account' && (\n <p style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', margin: '8px 0 0' }}>{notice.text}</p>\n )}\n </section>\n\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 12px' }}>修改用户名</h2>\n <form onSubmit={(e) => { e.preventDefault(); void changeUsername() }} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 新用户名\n <input type=\"text\" value={newUsername} onChange={(e) => setNewUsername(e.target.value)} autoComplete=\"username\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 当前密码\n <input type=\"password\" value={usernamePassword} onChange={(e) => setUsernamePassword(e.target.value)} autoComplete=\"current-password\" style={inputStyle} />\n </label>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n <button type=\"submit\" disabled={busy} style={primaryButtonStyle}>修改用户名</button>\n {notice?.owner === 'username' && <span style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{notice.text}</span>}\n </div>\n </form>\n </section>\n\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 12px' }}>修改密码</h2>\n <form onSubmit={(e) => { e.preventDefault(); void changePassword() }} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 当前密码\n <input type=\"password\" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} autoComplete=\"current-password\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 新密码\n <input type=\"password\" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} autoComplete=\"new-password\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 确认新密码\n <input type=\"password\" value={confirm} onChange={(e) => setConfirm(e.target.value)} autoComplete=\"new-password\" style={inputStyle} />\n </label>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n <button type=\"submit\" disabled={busy} style={primaryButtonStyle}>修改密码</button>\n {notice?.owner === 'password' && <span style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{notice.text}</span>}\n </div>\n </form>\n </section>\n </div>\n )\n}\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as { isLoopback?: boolean } | undefined\n if (connection !== undefined) {\n Object.defineProperty(connection, 'isLoopback', { configurable: true, get: () => true })\n }\n ctx.plugin({\n inject: ['slots', 'settingsScope'],\n apply: (sub: ClientContext): void => {\n sub.slots.inject('settings.section', () =>\n sub.slots.register({ name: 'settings.section', id: SECTION_ID, order: 100, label: () => '认证' }, AuthSection),\n )\n },\n })\n}\n","import type { DirectoryFlowOwnerProps } from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\n\ninterface NodeProfileView {\n id: string\n name: string\n type: 'local-host' | 'local-docker' | 'remote-ssh' | 'remote-docker'\n status?: { reachable?: boolean }\n}\n\ninterface DirEntry {\n name: string\n path: string\n isDirectory: boolean\n}\n\nconst inputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n padding: '0 10px',\n height: 32,\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: 8,\n fontSize: 14,\n fontFamily: 'inherit',\n background: 'var(--dsw-alias-bg-layer-1)',\n color: 'var(--dsw-alias-label-primary)',\n outline: 'none',\n}\nconst buttonStyle: CSSProperties = {\n boxSizing: 'border-box',\n height: 36,\n padding: '0 14px',\n borderRadius: 18,\n fontSize: 14,\n lineHeight: '22px',\n fontFamily: 'inherit',\n cursor: 'pointer',\n border: 'none',\n display: 'inline-flex',\n justifyContent: 'center',\n alignItems: 'center',\n gap: 4,\n}\nconst primaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n background: 'var(--dsw-alias-button-primary-fill)',\n color: 'var(--dsw-alias-label-primary-foreground)',\n}\nconst secondaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-primary)',\n background: 'transparent',\n}\nconst overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n background: 'var(--dsw-alias-overlay-mask, rgba(0,0,0,0.45))',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 1000,\n}\nconst dialogStyle: CSSProperties = {\n width: 'min(92vw, 560px)',\n maxWidth: '100%',\n maxHeight: '80vh',\n overflow: 'auto',\n overflowX: 'hidden',\n background: 'var(--dsw-alias-bg-layer-1)',\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: 12,\n padding: 20,\n boxSizing: 'border-box',\n color: 'var(--dsw-alias-label-primary)',\n}\n\nfunction joinPath(base: string, name: string): string {\n return `${base.replace(/\\/+$/, '')}/${name}`\n}\n\nexport function DirectoryFlowOccupant(props: DirectoryFlowOwnerProps): ReactElement | null {\n const [nodes, setNodes] = useState<NodeProfileView[]>([])\n const [nodeId, setNodeId] = useState<string>('')\n const [path, setPath] = useState('')\n const [entries, setEntries] = useState<DirEntry[]>([])\n const [error, setError] = useState<string | undefined>(undefined)\n const [busy, setBusy] = useState(false)\n const [newDirName, setNewDirName] = useState('')\n\n useEffect(() => {\n if (!props.open) return\n void (async () => {\n try {\n const res = await fetch('/api/dshb/nodes')\n if (!res.ok) return\n const data = (await res.json()) as { nodes?: NodeProfileView[] }\n setNodes((data.nodes ?? []).filter((n) => n.type === 'local-host' || n.status?.reachable === true))\n } catch {}\n })()\n }, [props.open])\n\n const browse = useCallback(async (id: string, dir: string) => {\n setError(undefined)\n try {\n const res = await fetch(`/api/dshb/nodes/${id}/browse?path=${encodeURIComponent(dir)}`)\n const data = (await res.json()) as { ok?: boolean; path?: string; entries?: DirEntry[]; error?: string }\n if (!res.ok || !data.ok) {\n setError(data.error ?? '目录读取失败')\n return\n }\n setPath(data.path ?? dir)\n setEntries((data.entries ?? []).filter((e) => e.isDirectory))\n } catch {\n setError('网络错误')\n }\n }, [])\n\n const selectNode = useCallback(\n (id: string) => {\n setNodeId(id)\n setPath('')\n setEntries([])\n setError(undefined)\n void browse(id, '/')\n },\n [browse],\n )\n\n const mkdir = useCallback(async () => {\n if (!newDirName.trim()) return\n setBusy(true)\n setError(undefined)\n try {\n const target = joinPath(path, newDirName.trim())\n const res = await fetch(`/api/dshb/nodes/${nodeId}/mkdir`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ path: target }),\n })\n const data = (await res.json()) as { ok?: boolean; error?: string }\n if (!res.ok || !data.ok) {\n setError(data.error ?? '创建目录失败')\n return\n }\n setNewDirName('')\n void browse(nodeId, target)\n } catch {\n setError('网络错误')\n } finally {\n setBusy(false)\n }\n }, [newDirName, nodeId, path, browse])\n\n const confirm = useCallback(async () => {\n if (!nodeId || !path) {\n setError('请选择节点与目录')\n return\n }\n setBusy(true)\n setError(undefined)\n try {\n const node = nodes.find((n) => n.id === nodeId)\n if (!node) return\n if (node.type === 'local-host') {\n props.onPicked(path)\n return\n }\n const res = await fetch('/api/dshb/workspaces/bind', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ nodeId, remotePath: path }),\n })\n const data = (await res.json()) as { ok?: boolean; mirrorPath?: string; error?: string }\n if (!res.ok || !data.ok || !data.mirrorPath) {\n setError(data.error ?? '工作区注册失败')\n return\n }\n props.onPicked(data.mirrorPath)\n } catch {\n setError('网络错误')\n } finally {\n setBusy(false)\n }\n }, [nodeId, path, nodes, props])\n\n if (!props.open) return null\n\n const node = nodes.find((n) => n.id === nodeId)\n\n return (\n <div style={overlayStyle} role=\"dialog\" aria-label=\"添加工作区\">\n <div style={dialogStyle}>\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>\n <h2 style={{ margin: 0, fontSize: 16, color: 'var(--dsw-alias-label-primary)' }}>添加工作区</h2>\n <button type=\"button\" onClick={props.onCancel} style={secondaryButtonStyle}>\n 关闭\n </button>\n </div>\n\n <label style={{ display: 'block', fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginBottom: 4 }}>工作节点</label>\n <select style={{ ...inputStyle, marginBottom: 14 }} value={nodeId} onChange={(e) => selectNode(e.target.value)} disabled={props.busy || busy}>\n <option value=\"\">选择节点…</option>\n {nodes.map((n) => {\n const typeLabel =\n n.type === 'local-host' ? '本地' : n.type === 'remote-ssh' ? '远程 SSH' : n.type === 'local-docker' ? '本地 Docker' : n.type === 'remote-docker' ? '远程 Docker' : n.type\n return (\n <option key={n.id} value={n.id}>\n {n.name}({typeLabel})\n </option>\n )\n })}\n </select>\n\n {nodeId !== '' && (\n <>\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>\n <label style={{ fontSize: 12, color: 'var(--dsw-alias-label-secondary)' }}>当前目录({node?.type === 'local-host' ? '本机' : '远端'})</label>\n <button type=\"button\" onClick={() => void browse(nodeId, dirnameOf(path))} disabled={busy} style={secondaryButtonStyle}>\n 上一级\n </button>\n </div>\n <div style={{ ...inputStyle, marginBottom: 8, height: 'auto', minHeight: 32, display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 2, cursor: 'pointer', lineHeight: '32px', overflow: 'hidden' }} onClick={() => void browse(nodeId, path)}>\n {path.split('/').filter(Boolean).length === 0 ? (\n <span style={{ opacity: 0.5 }}>根目录</span>\n ) : (\n <>\n <span onClick={(ev) => { ev.stopPropagation(); void browse(nodeId, '/') }} style={{ cursor: 'pointer', color: 'var(--dsw-alias-label-secondary)' }}>/</span>\n {path.split('/').filter(Boolean).map((seg, i, arr) => {\n const dir = '/' + arr.slice(0, i + 1).join('/')\n return (\n <span key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 2 }}>\n <span onClick={(ev) => { ev.stopPropagation(); void browse(nodeId, dir) }} style={{ cursor: 'pointer' }}>{seg}</span>\n {i < arr.length - 1 && <span style={{ opacity: 0.4 }}>/</span>}\n </span>\n )\n })}\n </>\n )}\n </div>\n <div style={{ maxHeight: 200, overflow: 'auto', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 6, marginBottom: 10 }}>\n {entries.map((e) => (\n <div key={e.path} onClick={() => void browse(nodeId, e.path)} style={{ padding: '7px 10px', cursor: 'pointer', fontSize: 13, borderRadius: 6, color: 'var(--dsw-alias-label-primary)', display: 'flex', alignItems: 'center', gap: 6, overflow: 'hidden' }} onMouseEnter={(ev) => (ev.currentTarget.style.background = 'var(--dsw-alias-interactive-bg-hover)')} onMouseLeave={(ev) => (ev.currentTarget.style.background = 'transparent')}>\n <span style={{ flexShrink: 0 }}>📁</span>\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{e.name}</span>\n </div>\n ))}\n {entries.length === 0 && <div style={{ padding: 12, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' }}>无子目录</div>}\n </div>\n <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>\n <input style={{ ...inputStyle, flex: 1, minWidth: 0 }} value={newDirName} onChange={(e) => setNewDirName(e.target.value)} placeholder=\"在当前目录下新建子目录…\" onKeyDown={(e) => { if (e.key === 'Enter') void mkdir() }} />\n <button type=\"button\" onClick={() => void mkdir()} disabled={busy || !newDirName.trim()} style={{ ...secondaryButtonStyle, flexShrink: 0, whiteSpace: 'nowrap' }}>\n 新建\n </button>\n </div>\n </>\n )}\n\n {error && <div style={{ fontSize: 13, color: 'var(--dsw-alias-state-error-primary)', marginBottom: 10 }}>{error}</div>}\n\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>\n <button type=\"button\" onClick={props.onCancel} style={secondaryButtonStyle}>\n 取消\n </button>\n <button type=\"button\" onClick={() => void confirm()} disabled={busy || props.busy || !nodeId || !path} style={primaryButtonStyle}>\n 设为工作区\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nfunction dirnameOf(path: string): string {\n const parts = path.split('/').filter(Boolean)\n parts.pop()\n return parts.length === 0 ? '/' : `/${parts.join('/')}`\n}\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n\n/**\n * dshb 移动端屏幕适配(参考 dsh-web-mobile 实现,仅保留页面适配与目录抽屉\n * 切换,不含文件浏览 / 导出会话日志等功能按钮)。\n *\n * 机制与上游一致:dsh 客户端在窄屏会自动给 AppFrame 加 data-sidebar-collapsed\n * 折叠侧边栏;这里把侧边栏列(frame 的第一个 grid 子元素)用 CSS 抽屉化\n * (绝对定位 + transform 移出/滑入),并在会话头部注入一个切换按钮调用\n * ctx.layout.toggleSidebar()。窄屏判定与上游一致(<1024 且粗指针,避免桌面\n * 小窗误触发)。\n */\nexport const MOBILE_QUERY = '(max-width: 1023px) and (pointer: coarse)'\n\n/** 纯视口宽度判定:与放宽后的 MOBILE_CSS 媒体查询一致(不依赖 pointer 类型)。 */\nconst NARROW_QUERY = '(max-width: 1023px)'\n\nconst FRAME_ATTR = 'data-dshb-mobile'\nconst COLLAPSED_ATTR = 'data-sidebar-collapsed'\n\nconst MOBILE_CSS = `\n/* 仅按视口宽度判定,不绑定 pointer:coarse —— 触屏笔记本 / DevTools 响应式模式\n 常报 pointer:fine,绑定 coarse 会让全部窄屏修复失效。抽屉相关选择器都挂在\n [data-dshb-mobile] 标记上,标记由 JS 的 coarse 判定添加,故放宽 CSS 不会误开抽屉。 */\n@media (max-width: 1023px) {\n html, body { touch-action: pan-y pinch-zoom !important; overscroll-behavior-x: none !important; -webkit-text-size-adjust: 100% !important; text-size-adjust: 100% !important; }\n\n [${FRAME_ATTR}=\"frame\"] {\n box-sizing: border-box !important;\n position: relative !important;\n grid-template-columns: minmax(0, 1fr) 0 0 !important;\n padding-top: env(safe-area-inset-top, 0px) !important;\n }\n\n /* 侧边栏列 -> 抽屉:默认移出屏幕,展开(无 collapsed)时滑入 */\n [${FRAME_ATTR}=\"frame\"] > :first-child {\n position: absolute !important;\n inset: 0 auto 0 0 !important;\n width: max-content;\n max-width: 92vw;\n z-index: 40 !important;\n transform: translateX(-110%);\n transition: transform .28s ease-in-out;\n background: var(--dsw-alias-bg-base, #ffffff);\n padding-top: env(safe-area-inset-top, 0px) !important;\n border-right: none !important;\n touch-action: pan-y pinch-zoom !important;\n }\n [${FRAME_ATTR}=\"frame\"]:not([${COLLAPSED_ATTR}]) > :first-child { transform: none !important; }\n @media (prefers-reduced-motion: reduce) {\n [${FRAME_ATTR}=\"frame\"] > :first-child { transition: none !important; }\n }\n\n /* 拖拽手柄在触屏上无用 */\n [data-side=\"sidebar\"], [data-side=\"details\"] { display: none !important; }\n\n /* 对话区:去桌面滚动条占位,收窄 gutter,字号降一档 */\n [data-phase] [class*=\"_scrollBody\"] { scrollbar-gutter: auto !important; scrollbar-width: none; }\n [data-phase] [class*=\"_scrollBody\"]::-webkit-scrollbar { display: none !important; width: 0; height: 0; }\n [data-phase] [class*=\"_scroll\"]:not([class*=\"_scrollBody\"]):has(p) { padding-left: 20px; padding-right: 20px; font-size: 15px !important; }\n [data-phase] [class*=\"_scroll\"]:not([class*=\"_scrollBody\"]):has(p) p,\n [data-phase] [class*=\"_scroll\"]:not([class*=\"_scrollBody\"]):has(p) li,\n [data-phase] [class*=\"_scroll\"]:not([class*=\"_scrollBody\"]):has(p) [class*=\"_text_\"] { font-size: 15px !important; }\n [data-phase] table { width: 100%; max-width: 100%; }\n [data-phase] th, [data-phase] td { max-width: none; min-width: 0; }\n [data-phase] [class*=\"_scroll\"]:not([class*=\"_scrollBody\"]) img { width: auto !important; max-width: 100% !important; height: auto !important; max-height: 220px !important; }\n [data-phase] [class*=\"_userStack\"], [data-phase] [class*=\"_userStack\"] [class*=\"_bubble\"] { box-sizing: border-box; width: fit-content; max-width: 100%; }\n [data-phase] [class*=\"_actions\"] { overflow: hidden; }\n\n /* 统计行(turns/steps/LLM/TPS):窄屏横向滚动,指标可滑动看全 */\n [${FRAME_ATTR}=\"stats\"] {\n display: flex !important;\n flex-wrap: nowrap !important;\n align-items: center;\n gap: 6px;\n overflow-x: auto !important;\n max-width: 100% !important;\n scrollbar-width: none;\n -webkit-overflow-scrolling: touch;\n }\n [${FRAME_ATTR}=\"stats\"]::-webkit-scrollbar { display: none; }\n [${FRAME_ATTR}=\"stats\"] > * { flex-shrink: 0 !important; white-space: nowrap !important; }\n [${FRAME_ATTR}=\"stats\"] { gap: 6px !important; }\n [class*=\"composerStack\"] { gap: 4px !important; }\n /* stats line 竖线分隔符(|)两边 margin 10px 太松,适度收紧 */\n [data-phase] [class*=\"_sep\"] { margin: 0 3px !important; }\n\n /* 每轮回复下方的统计文字(footer):收紧 actions 内部 gap 与气泡到 footer 间距,减少松散感 */\n [data-phase] [class*=\"_actions\"] { gap: 4px !important; }\n [data-phase] [class*=\"_actions\"] [class*=\"_timeEnd\"] { gap: 4px !important; padding-left: 6px !important; }\n [data-phase] [class*=\"userRow\"], [data-phase] [class*=\"assistantRow\"] { gap: 4px !important; }\n /* 点号(·)左右 margin 10px 太松,收紧并抵消前后空格 */\n [data-phase] [class*=\"_runTimeDot\"] { margin: 0 -2px !important; }\n /* footer 容器各项间距 16px 偏松 */\n [data-phase] [class*=\"_root\"][class*=\"osXY9a\"] { gap: 8px !important; }\n /* footer 统计行窄屏可横向滑动看全,不溢出页面 */\n [data-phase] [class*=\"_actions\"] { max-width: 100% !important; overflow-x: auto !important; scrollbar-width: none; touch-action: pan-x pan-y !important; }\n [data-phase] [class*=\"_actions\"]::-webkit-scrollbar { display: none; }\n\n /* 模型切换菜单:名称完整展示,长名称在菜单项内换行。 */\n [class*=\"_submenu_\"] [class*=\"_itemLabel_\"] {\n overflow: visible !important;\n text-overflow: clip !important;\n white-space: normal !important;\n overflow-wrap: anywhere;\n }\n\n /* 模型选择器(dsh-client-ui-model-selection,CSS 模块前缀 7KE1Ra_):\n 列表内模型名/描述默认 ellipsis 截断,改换行完整显示。除当前 hash 外,\n 再加 modelName 语义匹配,防止上游升级换 hash 后规则失效。\n 注意:必须用 break-word 而非 anywhere —— anywhere 让文字 min-content\n 宽度≈1 字符,配合全局 * {min-width:0} 会把 flex 行内名字列压到 1 字符宽,\n 出现\"一字符就换行\"。break-word 的 min-content 取最长词宽,只在真正\n 放不下时才断词。 */\n [class*=\"7KE1Ra_modelName\"],\n [class*=\"7KE1Ra_description\"],\n [class*=\"modelName\"] {\n white-space: normal !important;\n overflow-wrap: break-word;\n text-overflow: clip !important;\n overflow: visible !important;\n }\n\n /* 模型选择器弹窗:JS(installModelMenuPosition)在窄屏下改为 position:fixed\n 视口级浮层(fixed 不受祖先 overflow 裁剪),这里仅做静态兜底(JS 未生效时\n 仍从触发按钮左缘向右展开、宽度收进视口)。 */\n [class*=\"7KE1Ra_menu\"],\n [class*=\"_menu\"]:has([class*=\"modelName\"]) {\n left: 0 !important;\n right: auto !important;\n width: calc(100vw - 32px) !important;\n min-width: 0 !important;\n max-width: calc(100vw - 32px) !important;\n overflow-y: auto !important;\n max-height: calc(100vh - 120px) !important;\n }\n\n /* 工具调用执行结果/代码/JSON:窄屏可横向滑动。details 抽屉宽 min(92%,420px)\n 较窄,命令行输出/JSON 长行溢出被外层 overflow:hidden 裁剪看不到。命令行输出\n 改 white-space:pre(保持原格式不折行,像终端)+ overflow-x:auto 横向滑动;\n min-width:0+max-width:100% 约束宽度触发滚动。 */\n [data-phase] [class*=\"resultBlockText\"],\n [data-phase] [class*=\"payload\"]:not([class*=\"errorPayload\"]),\n [data-phase] [class*=\"sourceBlockContent\"] {\n white-space: pre !important;\n min-width: 0 !important;\n max-width: 100% !important;\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n touch-action: pan-x pan-y !important;\n }\n [data-phase] [class*=\"resultBlockText\"]::-webkit-scrollbar,\n [data-phase] [class*=\"payload\"]::-webkit-scrollbar,\n [data-phase] [class*=\"sourceBlockContent\"]::-webkit-scrollbar { display: none !important; }\n\n /* JSON 预览/代码块/promptDiff:横向滑动(不折行,保持格式) */\n [data-phase] [class*=\"jsonPayload\"],\n [data-phase] [class*=\"jsonPreview\"],\n [data-phase] [class*=\"promptDiff\"],\n [data-phase] [class*=\"overviewPreview\"],\n [data-phase] [class*=\"resultBlocks\"],\n [data-phase] pre {\n min-width: 0 !important;\n max-width: 100% !important;\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n touch-action: pan-x pan-y !important;\n }\n [data-phase] [class*=\"jsonPayload\"]::-webkit-scrollbar,\n [data-phase] [class*=\"jsonPreview\"]::-webkit-scrollbar,\n [data-phase] [class*=\"promptDiff\"]::-webkit-scrollbar,\n [data-phase] [class*=\"overviewPreview\"]::-webkit-scrollbar,\n [data-phase] [class*=\"resultBlocks\"]::-webkit-scrollbar,\n [data-phase] pre::-webkit-scrollbar { display: none !important; }\n\n /* dsh-client-ui-tool 工具卡片(Bash 等命令输出,class 前缀 o3BgMG_):\n bodyScroll/ioSection 只有 overflow-y:auto,横向溢出传到 row{overflow:hidden}\n 被裁剪且无法滑动。给输出容器链设 overflow-x:auto(不覆盖上游 overflow-y),\n 让终端格式长行在卡片内横向滑动。 */\n [data-phase] [class*=\"bodyScroll\"],\n [data-phase] [class*=\"ioCard\"],\n [data-phase] [class*=\"ioSection\"],\n [data-phase] [class*=\"terminalBody\"],\n [data-phase] [class*=\"codeBody\"] {\n min-width: 0 !important;\n max-width: 100% !important;\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n touch-action: pan-x pan-y !important;\n }\n [data-phase] [class*=\"bodyScroll\"]::-webkit-scrollbar,\n [data-phase] [class*=\"ioCard\"]::-webkit-scrollbar,\n [data-phase] [class*=\"ioSection\"]::-webkit-scrollbar,\n [data-phase] [class*=\"terminalBody\"]::-webkit-scrollbar,\n [data-phase] [class*=\"codeBody\"]::-webkit-scrollbar { display: none !important; }\n\n /* 终端组件(dsh-web-frontend,CSS 模块前缀 1gdtu_):多行命令上游按行渲染成\n 多个 _promptLine_>_command_ 元素,行级不能各自设 overflow(会变成每行独立\n 滚动)。唯一横向滚动容器是公共祖先 _header_1gdtu_32(本就 overflow-y:auto):\n 行链 _prompt_/_promptLine_/_command_ 全部撑到 max-content 且 overflow 可见,\n 长行内容溢出传入 _header_ 的 scrollWidth,所有行随 _header_ 同步滚动。\n _output_1gdtu_156(输出区)同理以其为唯一容器,_line_ 撑宽不裁剪。 */\n [data-phase] [class*=\"_header_1gdtu_\"] {\n min-width: 0 !important;\n max-width: 100% !important;\n overflow-x: auto !important;\n overscroll-behavior-x: contain;\n touch-action: pan-x pan-y !important;\n }\n [data-phase] [class*=\"_prompt_1gdtu_\"],\n [data-phase] [class*=\"_promptLine_1gdtu_\"],\n [data-phase] [class*=\"_command_1gdtu_\"] {\n min-width: max-content !important;\n max-width: none !important;\n overflow: visible !important;\n /* Android Chrome TextAutosizer 按块宽计算 font boosting,max-content 行宽不一\n 导致各行字号参差;max-height 是其豁免条件,禁用放大并锁定继承字号。 */\n max-height: 999999px !important;\n }\n [data-phase] [class*=\"_command_1gdtu_\"] {\n display: inline-block;\n text-overflow: clip !important;\n white-space: pre !important;\n }\n [data-phase] [class*=\"_copyButton_1gdtu_\"] { position: sticky !important; right: 0; }\n [data-phase] [class*=\"_output_1gdtu_\"] {\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n touch-action: pan-x pan-y !important;\n }\n [data-phase] [class*=\"_output_1gdtu_\"]::-webkit-scrollbar { display: none !important; }\n [data-phase] [class*=\"_line_1gdtu_\"] {\n min-width: max-content !important;\n overflow-x: visible !important;\n max-height: 999999px !important;\n }\n\n /* 复制按钮周边也允许纵向页面手势,避免代码/总结区域形成触控死区。 */\n [data-phase] [class*=\"_copyButton_\"] { touch-action: manipulation !important; }\n [data-phase] [class*=\"_copyAnchor_\"],\n [data-phase] [class*=\"_body_1ye18_\"],\n [data-phase] [class*=\"_root_4qrvp_\"] { touch-action: pan-x pan-y !important; }\n\n /* 上下文用量弹窗(ContextMeter .JObwrW_panel):上游 dsh-client-ui-trajectory 的\n [class*=\"panel\"] 全局规则给它加了 max-width:100%!important,命中本弹窗,使宽度\n 从 264px 塌缩到包含块(.root 28px)宽,内容竖排成竖条。窄屏解除该误伤。 */\n .JObwrW_panel { max-width: none !important; overflow-x: visible !important; }\n\n /* 头部:标题省略,tab 条横向滚动 */\n [${FRAME_ATTR}=\"frame\"] [data-phase] header { padding-left: 44px; padding-right: 8px; }\n [${FRAME_ATTR}=\"frame\"] [data-phase] header [class*=\"_crumbs\"] { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap !important; }\n [${FRAME_ATTR}=\"frame\"] [data-phase] header [role=\"tablist\"] { flex-wrap: nowrap; gap: 0 16px; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }\n [${FRAME_ATTR}=\"frame\"] [data-phase] header [role=\"tablist\"]::-webkit-scrollbar { display: none; }\n [${FRAME_ATTR}=\"frame\"] [data-phase] header [role=\"tablist\"] > button { flex-shrink: 0; white-space: nowrap; }\n\n /* 设置对话框:窄屏近全宽 sheet(排除导出等普通对话框与目录选择器) */\n [aria-modal=\"true\"]:has(> :first-child > :last-child > button):not(:has([role=\"navigation\"])):not(:has([class*=\"ZuhsRW\"])) {\n position: absolute !important;\n left: 8px !important;\n top: calc(env(safe-area-inset-top, 0px) + 12px) !important;\n width: calc(100vw - 16px) !important;\n max-width: calc(100vw - 16px) !important;\n height: auto !important;\n max-height: calc(100dvh - 24px - env(safe-area-inset-top, 0px)) !important;\n flex-direction: column !important;\n border-radius: 14px !important;\n }\n [aria-modal=\"true\"]:not(:has(> :first-child > :last-child > button)) { max-width: calc(100vw - 32px) !important; }\n /* nav:横排换行(避免 CJK 标签竖排楼梯状),隐藏冗余 caption */\n [aria-modal=\"true\"]:has(> :first-child > :last-child > button):not(:has([role=\"navigation\"])):not(:has([class*=\"ZuhsRW\"])) > :first-child {\n width: 100%;\n flex-direction: row !important;\n align-items: center;\n gap: 6px;\n padding: 10px 12px 8px;\n }\n [aria-modal=\"true\"]:has(> :first-child > :last-child > button):not(:has([role=\"navigation\"])):not(:has([class*=\"ZuhsRW\"])) > :first-child > :first-child { display: none !important; }\n [aria-modal=\"true\"] [class*=\"_navList\"] { flex: 1 1 auto; min-width: 0; flex-direction: row !important; flex-wrap: wrap; gap: 6px; overflow: visible; }\n [aria-modal=\"true\"] [class*=\"_navList\"] > button { flex-shrink: 0; white-space: nowrap; }\n /* 外观模式卡片横排 */\n [aria-modal=\"true\"] [class*=\"_cubeRow\"] { gap: 6px; }\n [aria-modal=\"true\"] [class*=\"_cubeRow\"] > * { flex: 1 1 0; flex-direction: row !important; align-items: center; justify-content: center; gap: 6px; padding: 10px 8px; min-height: 0; }\n /* section 填充 sheet 宽度 */\n [aria-modal=\"true\"] [class*=\"_section\"] { width: 100% !important; max-width: none !important; }\n [aria-modal=\"true\"]:has(> :first-child > :last-child > button):not(:has([role=\"navigation\"])):not(:has([class*=\"ZuhsRW\"])) > :last-child { flex: 1 1 auto; min-height: 0; }\n [aria-modal=\"true\"]:has(> :first-child > :last-child > button):not(:has([role=\"navigation\"])):not(:has([class*=\"ZuhsRW\"])) > :last-child > :last-child { padding: 0 12px 24px; }\n\n /* 切换按钮:固定头部左侧 */\n [${FRAME_ATTR}=\"toggle\"] {\n position: absolute !important;\n left: 8px !important;\n top: 12px !important;\n z-index: 2 !important;\n display: inline-flex !important;\n align-items: center; justify-content: center;\n width: 28px; height: 28px;\n padding: 0; border: none; background: none; cursor: pointer; color: inherit;\n -webkit-tap-highlight-color: transparent;\n }\n\n /* 首页/hero(无活跃会话,session header 不存在)的抽屉切换 FAB:\n 浮在顶部相机带下方,避免与 hero 头部内容重叠 */\n [${FRAME_ATTR}=\"fab\"] {\n position: absolute !important;\n top: calc(env(safe-area-inset-top, 0px) + 72px) !important;\n left: 10px !important;\n z-index: 21 !important;\n display: inline-flex !important;\n align-items: center; justify-content: center;\n width: 38px; height: 38px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .12)) !important;\n border-radius: 50% !important;\n background: var(--dsw-alias-button-floating-fill, #ffffff) !important;\n color: var(--dsw-alias-label-primary, inherit) !important;\n cursor: pointer !important;\n box-shadow: 0 2px 12px rgba(0, 0, 0, .18) !important;\n -webkit-tap-highlight-color: transparent;\n }\n}\n\n/* 宽屏 / 鼠标指针:隐藏切换按钮 */\n@media (min-width: 1024px), (pointer: fine) {\n [${FRAME_ATTR}=\"toggle\"], [${FRAME_ATTR}=\"fab\"] { display: none !important; }\n}\n`\n\nfunction findFrame(): HTMLElement | null {\n return document.querySelector('[data-shell-overlay]')?.parentElement ?? null\n}\n\nfunction getFrame(): HTMLElement | null {\n return document.querySelector(`[${FRAME_ATTR}=\"frame\"]`) ?? findFrame()\n}\n\n/** viewport-fit=cover:让 safe-area-inset 生效,内容避开刘海/状态栏。 */\nfunction installViewport(ctx: ClientContext): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(MOBILE_QUERY)\n const viewport = document.querySelector<HTMLMetaElement>('meta[name=\"viewport\"]')\n if (viewport === null) return () => {}\n const original = viewport.content\n const sync = (): void => {\n viewport.content = narrow.matches\n ? 'width=device-width, initial-scale=1, viewport-fit=cover'\n : original\n }\n sync()\n narrow.addEventListener('change', sync)\n return () => {\n narrow.removeEventListener('change', sync)\n viewport.content = original\n }\n }, 'dshb-mobile: viewport')\n}\n\nfunction installStyles(ctx: ClientContext): void {\n ctx.effect(() => {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dshb-mobile'\n tag.textContent = MOBILE_CSS\n document.head.appendChild(tag)\n // 保持在 head 最后,确保覆盖宿主样式\n setTimeout(() => { if (tag.isConnected) document.head.appendChild(tag) }, 0)\n return () => { tag.remove() }\n }, 'dshb-mobile: styles')\n}\n\n/** 给 AppFrame 打标记(drawer CSS 选择器挂载点),窄屏才打。 */\nfunction installFrameMarker(ctx: ClientContext): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(MOBILE_QUERY)\n let frame: HTMLElement | null = null\n const clear = (): void => {\n if (frame !== null) frame.removeAttribute(FRAME_ATTR)\n frame = null\n }\n const ensure = (): void => {\n if (!narrow.matches) return\n frame = findFrame()\n if (frame !== null && !frame.hasAttribute(FRAME_ATTR)) frame.setAttribute(FRAME_ATTR, 'frame')\n }\n ensure()\n const mo = new MutationObserver(ensure)\n mo.observe(document.documentElement, { childList: true, subtree: true })\n const onChange = (): void => { if (narrow.matches) ensure(); else clear() }\n narrow.addEventListener('change', onChange)\n return () => {\n mo.disconnect()\n narrow.removeEventListener('change', onChange)\n clear()\n }\n }, 'dshb-mobile: frame marker')\n}\n\n/**\n * 抽屉打开时覆盖内容区的半透明遮罩,点击关闭;Escape 同样关闭;\n * 点击抽屉内的会话/导航项后关闭抽屉。\n */\nfunction installDrawerInteractions(ctx: ClientContext, toggleSidebar: () => void): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(MOBILE_QUERY)\n let backdrop: HTMLElement | null = null\n const drawerOpen = (): boolean => {\n const frame = getFrame()\n return narrow.matches && frame !== null && !frame.hasAttribute(COLLAPSED_ATTR)\n }\n const sync = (): void => {\n if (drawerOpen() && backdrop === null) {\n backdrop = document.createElement('div')\n backdrop.setAttribute(`${FRAME_ATTR}-backdrop`, '')\n backdrop.style.cssText = 'position:fixed;inset:0;z-index:39;background:rgba(0,0,0,.4);-webkit-tap-highlight-color:transparent;'\n backdrop.addEventListener('click', () => toggleSidebar())\n document.body.appendChild(backdrop)\n } else if (!drawerOpen() && backdrop !== null) {\n backdrop.remove()\n backdrop = null\n }\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape') return\n if (document.querySelector('[aria-modal=\"true\"]') !== null) return\n if (drawerOpen()) toggleSidebar()\n }\n const onClick = (event: MouseEvent): void => {\n if (!drawerOpen()) return\n const target = event.target\n if (!(target instanceof Element)) return\n const drawer = document.querySelector(`[${FRAME_ATTR}=\"frame\"] > :first-child`)\n if (drawer === null || !drawer.contains(target)) return\n if (target.closest('button, a, [role=\"button\"]') !== null) return\n if (target.closest('[class*=\"newSession\"], [class*=\"sessionRow\"], [role=\"treeitem\"], [class*=\"searchResult\"]') !== null) toggleSidebar()\n }\n sync()\n const mo = new MutationObserver(sync)\n mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: [COLLAPSED_ATTR] })\n document.addEventListener('keydown', onKeyDown, true)\n document.addEventListener('click', onClick, true)\n narrow.addEventListener('change', sync)\n return () => {\n mo.disconnect()\n document.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('click', onClick, true)\n narrow.removeEventListener('change', sync)\n backdrop?.remove()\n }\n }, 'dshb-mobile: drawer interactions')\n}\n\n/** 对话/输入框下方的统计行(turns/steps/LLM/TPS):hashed class 无法直接选,按文本特征标记后由 CSS 横向滚动,避免溢出。 */\nfunction installStatsLine(ctx: ClientContext): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(MOBILE_QUERY)\n // 统计行文本特征(\"3 轮 · 5 步 · LLM 2.1s · 缓存命中 80%\" 等),hashed class 无法直接选,按文本识别。\n // 含\"轮/步/turns/steps\"即可(footer 不含这些),无需额外排除 footer。\n const STATS_LINE_RE = /(\\d+\\s*轮|\\d+\\s*步|\\bturns\\b|\\bsteps\\b)/\n const isStatsText = (el: Element): boolean => {\n const text = (el.textContent ?? '').trim()\n if (text.length === 0 || text.length > 160) return false\n if (!STATS_LINE_RE.test(text)) return false\n if (el.querySelector('textarea, input, button, select, a') !== null) return false\n return true\n }\n const mark = (): void => {\n if (!narrow.matches) return\n // 清除失效标记:行容器仍含统计特征(轮/步)才保留\n document.querySelectorAll(`[${FRAME_ATTR}=\"stats\"]`).forEach((el) => {\n if (!el.isConnected || !STATS_LINE_RE.test((el.textContent ?? '').trim())) el.removeAttribute(FRAME_ATTR)\n })\n // 候选:含统计特征的短文本元素(全文档搜索,含 composer 内的 stats line),取最浅容器标记\n // 排除 display:contents(无 box,overflow 无效,会误占最浅候选)\n const cands = Array.from(document.querySelectorAll('span, div')).filter((el) => {\n if (!isStatsText(el)) return false\n if (getComputedStyle(el).display === 'contents') return false\n return true\n })\n for (const el of cands) {\n if (el.hasAttribute(FRAME_ATTR)) continue\n let anc = el.parentElement\n let hasOuterCand = false\n while (anc) {\n if (cands.includes(anc)) { hasOuterCand = true; break }\n anc = anc.parentElement\n }\n if (!hasOuterCand) {\n // 标最浅含统计的容器本身(整行),使其可横向滚动\n if (!el.hasAttribute(FRAME_ATTR)) el.setAttribute(FRAME_ATTR, 'stats')\n }\n }\n }\n mark()\n const mo = new MutationObserver(() => { if (narrow.matches) mark() })\n mo.observe(document.documentElement, { childList: true, subtree: true })\n narrow.addEventListener('change', mark)\n return () => {\n mo.disconnect()\n narrow.removeEventListener('change', mark)\n }\n }, 'dshb-mobile: stats line')\n}\n\n/** 浮层 fixed 定位需管理的内联样式键,清理时逐一移除以交还上游 CSS。 */\nconst SHEET_PROPS = [\n 'position', 'width', 'left', 'right', 'top', 'bottom',\n 'min-width', 'max-width', 'max-height', 'overflow-y', 'box-sizing',\n] as const\n\n/**\n * 把挂在 composer 内部的绝对定位浮层统一改造成 position:fixed 视口级浮层:\n * 水平居中(左右各留 8px,绝不贴边/溢出),高度按视口比例封顶并内容滚动,\n * 触发元素上方空间足够则向上展开、否则向下展开,四边都约束在视口内。\n * fixed 不受祖先 overflow 裁剪(祖先带 transform 时失效)。\n */\nfunction positionFixedSheet(\n el: HTMLElement,\n rootRect: DOMRect,\n maxWidth: number,\n maxHeightRatio: number,\n): void {\n const margin = 8\n const vw = window.innerWidth\n const vh = window.innerHeight\n const width = Math.max(0, Math.min(maxWidth, vw - margin * 2))\n const left = Math.round((vw - width) / 2)\n const maxH = Math.round(vh * maxHeightRatio)\n const spaceAbove = rootRect.top - margin\n const spaceBelow = vh - rootRect.bottom - margin\n const expandUp = spaceAbove >= 140 && spaceAbove >= spaceBelow\n let top: number\n if (expandUp) {\n const h = Math.min(maxH, Math.max(spaceAbove, 140))\n top = rootRect.top - margin - h\n } else {\n top = rootRect.bottom + margin\n }\n top = Math.max(margin, top)\n el.style.setProperty('position', 'fixed', 'important')\n el.style.setProperty('box-sizing', 'border-box', 'important')\n el.style.setProperty('width', `${width}px`, 'important')\n el.style.setProperty('left', `${left}px`, 'important')\n el.style.setProperty('right', 'auto', 'important')\n el.style.setProperty('top', `${top}px`, 'important')\n el.style.setProperty('bottom', 'auto', 'important')\n el.style.setProperty('min-width', '0', 'important')\n el.style.setProperty('max-width', 'none', 'important')\n el.style.setProperty('max-height', `${maxH}px`, 'important')\n el.style.setProperty('overflow-y', 'auto', 'important')\n}\n\n/**\n * 一级 pane(Model/Effort 单元格小菜单)紧凑浮层:右缘对齐触发按钮右缘\n * (面板右缘 x = 按钮右缘 x,钳制在视口内 8px 起,避免宽面板超出屏幕左缘时\n * 被甩到左边贴边),向上展开、空间不足向下展开;宽度跟随内容\n * (max-content,封顶 min(420, vw-16)),避免小菜单被全宽 sheet 拉远到\n * 屏幕中间。测量与定位在同一任务内同步完成,无闪烁。\n */\nfunction positionCompactPanel(el: HTMLElement, root: HTMLElement, rootRect: DOMRect): void {\n const margin = 8\n const vw = window.innerWidth\n // 锚定触发按钮本身(root 包着按钮,取按钮 rect 更准;换 hash 后按语义类找)\n const trigger =\n root.querySelector<HTMLElement>('[class*=\"7KE1Ra_trigger\"], [class*=\"_trigger\"]') ?? root\n const anchorRect = trigger === root ? rootRect : trigger.getBoundingClientRect()\n el.style.setProperty('position', 'fixed', 'important')\n el.style.setProperty('box-sizing', 'border-box', 'important')\n el.style.setProperty('left', '0', 'important')\n el.style.setProperty('top', '0', 'important')\n el.style.setProperty('right', 'auto', 'important')\n el.style.setProperty('bottom', 'auto', 'important')\n el.style.setProperty('width', 'max-content', 'important')\n el.style.setProperty('min-width', '0', 'important')\n el.style.setProperty('max-width', `${Math.min(420, vw - margin * 2)}px`, 'important')\n el.style.setProperty('max-height', 'none', 'important')\n el.style.setProperty('overflow-y', 'visible', 'important')\n const w = el.offsetWidth\n const h = el.offsetHeight\n const panelRightX = Math.min(anchorRect.right, vw - margin)\n const expandUp = anchorRect.top - margin - h >= margin\n const top = Math.max(margin, expandUp ? anchorRect.top - margin - h : anchorRect.bottom + margin)\n const left = Math.max(margin, panelRightX - w)\n el.style.setProperty('left', `${left}px`, 'important')\n el.style.setProperty('top', `${top}px`, 'important')\n el.style.setProperty('width', `${w}px`, 'important')\n}\n\n/**\n * 模型名多为纯连字符词(DeepSeek-V4-Flash-Vision-Exp);white-space:normal 下\n * 每个 \"-\" 都是软断点,名字一旦超列宽就在第一个连字符处断行(\"换行太早\")。\n * 把 \"-\" 就地换成不换行连字符 U+2011:放得下就整行显示,真超宽时仍由\n * overflow-wrap:break-word 兜底任意断。只改文本节点 data、不换节点,\n * 避免与 React 文本协调冲突;React 重渲染重置文本后由 MutationObserver\n * 重新应用(幂等:换过之后不再有 \"-\")。\n */\nfunction applyNonBreakingHyphens(menu: HTMLElement): void {\n for (const el of Array.from(menu.querySelectorAll<HTMLElement>('[class*=\"modelName\"]'))) {\n const tn = el.firstChild\n if (tn && tn.nodeType === Node.TEXT_NODE && tn.nodeValue !== null && tn.nodeValue.includes('-')) {\n tn.nodeValue = tn.nodeValue.replace(/-/g, '\\u2011')\n }\n }\n}\n\n/**\n * 模型名\"整行显示、不换行、不截断\":窄屏下把名字列里的所有 .modelName 设为\n * nowrap,并按\"最宽名字 vs 可用列宽\"统一缩小字号(下限 11px,上限 14px),\n * 让最长的名字也恰好放得下——从根上消除\"换行太早\"。只测一次最宽名字\n * (scrollWidth 一次布局读取),缩放结果应用到全部名字,保持同一字号。\n * 调用方按 vw + 名字集合签名缓存,避免每次 DOM 变更都强制作废布局。\n */\nfunction fitModelNameFont(menu: HTMLElement): void {\n const names = Array.from(menu.querySelectorAll<HTMLElement>('[class*=\"modelName\"]'))\n if (names.length === 0) return\n const col = names[0].parentElement\n if (col === null) return\n const colW = col.clientWidth - 4\n if (colW <= 0) return\n const base = 14\n let longest = names[0]\n for (const n of names) {\n if ((n.textContent?.length ?? 0) > (longest.textContent?.length ?? 0)) longest = n\n }\n const prevWs = longest.style.whiteSpace\n longest.style.whiteSpace = 'nowrap'\n longest.style.fontSize = `${base}px`\n const natural = longest.scrollWidth\n longest.style.whiteSpace = prevWs\n longest.style.fontSize = ''\n const fs = natural > colW ? Math.max(11, Math.min(base, Math.round(base * (colW / natural)))) : base\n for (const n of names) {\n n.style.setProperty('white-space', 'nowrap', 'important')\n n.style.setProperty('font-size', `${fs}px`, 'important')\n }\n}\n\n/** 宽屏(窄屏判定不成立)时复位名字列内联样式,交还上游 CSS(nowrap+ellipsis)。 */\nfunction clearModelNameFit(menu: HTMLElement | null | undefined): void {\n if (menu === null || menu === undefined) return\n for (const n of Array.from(menu.querySelectorAll<HTMLElement>('[class*=\"modelName\"]'))) {\n n.style.removeProperty('white-space')\n n.style.removeProperty('font-size')\n }\n}\n\n/** 清除 positionFixedSheet 写入的内联样式,交还上游/移动端 CSS 兜底。 */\nfunction clearFixedSheet(el: HTMLElement | null | undefined): void {\n if (el === null || el === undefined) return\n for (const key of SHEET_PROPS) el.style.removeProperty(key)\n}\n\n/**\n * 模型选择弹窗(dsh-client-ui-model-selection)是两级 pane 的同一个 `_7KE1Ra_menu`\n * 元素:一级 Model/Effort 单元格列表(cellLabel),点击后切到模型列表(modelName)\n * 或档位列表。上游默认 `position:absolute;right:0` 贴触发按钮右缘,触发按钮在\n * 可横向滚动的 stats 行内时菜单会整体出屏。两级 pane 都必须接管(不能只按\n * modelName 匹配,否则一级 pane 漏掉)。窄屏下:一级小菜单走紧凑浮层\n * (右缘贴按钮、向上展开,避免全宽 sheet 把小菜单拉到屏幕中间离按钮太远);\n * 二级列表走视口级居中 fixed sheet(左右各 8px、高度 45% 视口封顶、\n * 内容滚动、上方空间不足时向下展开)。\n */\nfunction installModelMenuPosition(ctx: ClientContext): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(NARROW_QUERY)\n // 多 tab 场景可能并存多个模型选择器(各自一个 _menu);只处理可见的,\n // 避免 document.querySelector 命中隐藏 tab 的菜单而漏掉用户正在看的那个。\n const findMenus = (): HTMLElement[] => {\n const byHash = Array.from(document.querySelectorAll<HTMLElement>('[class*=\"7KE1Ra_menu\"]'))\n if (byHash.length > 0) return byHash\n // 上游升级换 hash 后回退:语义类匹配(cellLabel 命中一级 pane,modelName 命中二级)\n return Array.from(document.querySelectorAll<HTMLElement>('[class*=\"_menu\"]')).filter((el) =>\n el.querySelector('[class*=\"cellLabel\"], [class*=\"modelName\"]') !== null,\n )\n }\n const findTrigger = (menu: HTMLElement): HTMLElement | null => {\n if (menu.id) {\n const controlled = Array.from(document.querySelectorAll<HTMLElement>('[aria-controls]')).find(\n (el) => el.getAttribute('aria-controls') === menu.id,\n )\n if (controlled) return controlled\n }\n return document.querySelector<HTMLElement>('[class*=\"7KE1Ra_trigger\"][aria-expanded=\"true\"]')\n }\n let fittedVw = 0\n let fittedSig = -1\n let fittedMenus: HTMLElement[] = []\n const sync = (): void => {\n const menus = findMenus()\n if (!narrow.matches) {\n // Desktop: upstream place() owns menu positioning via inline left/top.\n // Only clear DSHB's !important overrides when transitioning FROM narrow\n // mode (fittedMenus non-empty). Clearing on every MutationObserver tick\n // would strip upstream's menuPos and drop the menu off-screen.\n if (fittedMenus.length > 0) {\n for (const menu of fittedMenus) {\n clearFixedSheet(menu)\n clearModelNameFit(menu)\n }\n fittedVw = 0\n fittedSig = -1\n fittedMenus = []\n window.dispatchEvent(new Event('resize'))\n }\n return\n }\n let vw = 0\n let sig = 0\n const modelMenus: HTMLElement[] = []\n for (const menu of menus) {\n // 0.1.5 renders the menu through createPortal(..., document.body), so\n // parentElement is body rather than the trigger root. Match aria-controls\n // to the portal menu id to find the exact trigger across multiple tabs.\n const trigger = findTrigger(menu)\n if (trigger === null) continue\n const rect = menu.getBoundingClientRect()\n // 跳过隐藏 tab / 未打开的菜单(0 尺寸或祖先 display:none)\n if (rect.width === 0 && rect.height === 0) continue\n const triggerRect = trigger.getBoundingClientRect()\n if (menu.querySelector('[class*=\"modelName\"]')) {\n positionFixedSheet(menu, triggerRect, Number.POSITIVE_INFINITY, 0.45)\n applyNonBreakingHyphens(menu)\n modelMenus.push(menu)\n vw = window.innerWidth\n for (const n of menu.querySelectorAll<HTMLElement>('[class*=\"modelName\"]')) {\n sig += n.textContent?.length ?? 0\n }\n } else {\n positionCompactPanel(menu, trigger, triggerRect)\n }\n }\n // 菜单关闭再打开时 React 会重建菜单元素(新元素没有任何内联样式),\n // 所以缓存必须含元素身份:vw / 名字集合 / 菜单实例任一变化都要重测,\n // 否则重开的菜单会漏掉 nowrap+缩字而再次换行。\n const needFit = modelMenus.some((m) => !fittedMenus.includes(m))\n if (vw > 0 && (needFit || vw !== fittedVw || sig !== fittedSig)) {\n for (const menu of modelMenus) fitModelNameFont(menu)\n fittedVw = vw\n fittedSig = sig\n fittedMenus = modelMenus\n }\n }\n sync()\n const mo = new MutationObserver(() => requestAnimationFrame(sync))\n mo.observe(document.documentElement, { childList: true, subtree: true })\n narrow.addEventListener('change', sync)\n window.addEventListener('resize', sync)\n window.addEventListener('scroll', sync, true)\n return () => {\n mo.disconnect()\n narrow.removeEventListener('change', sync)\n window.removeEventListener('resize', sync)\n window.removeEventListener('scroll', sync, true)\n }\n }, 'dshb-mobile: model menu position')\n}\n\n/**\n * 上下文用量弹窗(ContextMeter .JObwrW_panel,dsh-client-ui-conversation)上游是\n * `position:absolute; bottom:calc(100%+8px); right:0` 挂 .JObwrW_root 内。窄屏下\n * 改为 fixed 视口浮层,右缘锚定触发按钮右缘、向上展开(用户已验证该形态正常,勿改)。\n */\nfunction installContextPanelPosition(ctx: ClientContext): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(NARROW_QUERY)\n const sync = (): void => {\n if (!narrow.matches) return\n const panels = document.querySelectorAll<HTMLElement>('[class*=\"JObwrW_panel\"]')\n for (const panel of Array.from(panels)) {\n const root = panel.parentElement\n if (root === null || !root.className.includes('JObwrW_root')) continue\n const rootRect = root.getBoundingClientRect()\n if (rootRect.width === 0 && rootRect.height === 0) continue\n const gap = 8\n panel.style.setProperty('position', 'fixed', 'important')\n panel.style.setProperty('top', 'auto', 'important')\n panel.style.setProperty('bottom', `${window.innerHeight - rootRect.top + gap}px`, 'important')\n panel.style.setProperty('left', 'auto', 'important')\n panel.style.setProperty('right', `${Math.max(gap, window.innerWidth - rootRect.right)}px`, 'important')\n panel.style.setProperty('max-height', `${Math.max(160, rootRect.top - gap * 2)}px`, 'important')\n panel.style.setProperty('overflow-y', 'auto', 'important')\n }\n }\n sync()\n const mo = new MutationObserver(() => requestAnimationFrame(sync))\n mo.observe(document.documentElement, { childList: true, subtree: true })\n narrow.addEventListener('change', sync)\n window.addEventListener('resize', sync)\n window.addEventListener('scroll', sync, true)\n return () => {\n mo.disconnect()\n narrow.removeEventListener('change', sync)\n window.removeEventListener('resize', sync)\n window.removeEventListener('scroll', sync, true)\n }\n }, 'dshb-mobile: context panel position')\n}\n\ninterface LayoutLike { toggleSidebar(): void }\ninterface SlotsLike {\n inject(name: string, fn: () => unknown): unknown\n register(spec: Record<string, unknown>, component: unknown): unknown\n}\n\n/** 目录抽屉切换按钮(仅切换,不含文件浏览/日志导出等功能按钮)。 */\nfunction DrawerToggle({ toggleSidebar }: { toggleSidebar: () => void }) {\n return (\n <button type=\"button\" data-dshb-mobile=\"toggle\" aria-label=\"打开目录\" title=\"打开目录\" onClick={() => toggleSidebar()}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"1.5\" y=\"2.5\" width=\"13\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M6 2.5v11\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n </button>\n )\n}\n\nconst PANEL_ICON_SVG =\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">' +\n '<rect x=\"1.5\" y=\"2.5\" width=\"13\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.5\"/>' +\n '<path d=\"M6 2.5v11\" stroke=\"currentColor\" strokeWidth=\"1.5\"/>' +\n '</svg>'\n\n/**\n * 首页 / hero(无活跃会话)没有 session header,header 上的切换按钮不渲染;\n * 这里在 frame 上注入一个浮动切换按钮(FAB),仅 hero 且抽屉收起时显示。\n */\nfunction installFab(ctx: ClientContext, toggleSidebar: () => void): void {\n ctx.effect(() => {\n const narrow = window.matchMedia(MOBILE_QUERY)\n const FAB_ATTR = 'fab'\n const heroPhase = (): boolean => document.querySelector('[data-phase=\"active\"]') === null\n const drawerOpen = (): boolean => {\n const frame = getFrame()\n return frame !== null && !frame.hasAttribute(COLLAPSED_ATTR)\n }\n let fab: HTMLButtonElement | null = null\n const sync = (): void => {\n if (!narrow.matches || !heroPhase() || drawerOpen()) {\n if (fab !== null) {\n fab.remove()\n fab = null\n }\n return\n }\n const frame = getFrame()\n if (frame === null) return\n if (fab !== null && fab.parentElement === frame) return\n fab?.remove()\n fab = document.createElement('button')\n fab.type = 'button'\n fab.setAttribute(FRAME_ATTR, FAB_ATTR)\n fab.setAttribute('aria-label', '打开目录')\n fab.setAttribute('title', '打开目录')\n fab.innerHTML = PANEL_ICON_SVG\n fab.addEventListener('click', toggleSidebar)\n frame.appendChild(fab)\n }\n sync()\n const mo = new MutationObserver(sync)\n mo.observe(document.documentElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['data-phase', 'data-sidebar-collapsed', 'class'],\n })\n narrow.addEventListener('change', sync)\n return () => {\n mo.disconnect()\n narrow.removeEventListener('change', sync)\n fab?.remove()\n }\n }, 'dshb-mobile: hero fab')\n}\n\n/** 安装移动端适配:样式 + viewport + 抽屉标记 + 抽屉交互 + 切换按钮。 */\nexport function installMobile(ctx: ClientContext): void {\n installStyles(ctx)\n installViewport(ctx)\n installFrameMarker(ctx)\n installStatsLine(ctx)\n installModelMenuPosition(ctx)\n installContextPanelPosition(ctx)\n const layout = (ctx as unknown as { layout?: LayoutLike }).layout\n const slots = (ctx as unknown as { slots?: SlotsLike }).slots\n if (layout && typeof layout.toggleSidebar === 'function') {\n const toggleSidebar = (): void => layout.toggleSidebar()\n installDrawerInteractions(ctx, toggleSidebar)\n installFab(ctx, toggleSidebar)\n }\n if (slots && layout) {\n slots.inject('conversation.session.header.actions', () =>\n slots.register(\n {\n name: 'conversation.session.header.actions',\n id: 'dshb-mobile-toggle',\n order: 10,\n inject: () => ({ toggleSidebar: () => layout.toggleSidebar() }),\n },\n DrawerToggle,\n ),\n )\n }\n}\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { DirectoryFlowOccupant } from './directory-flow.js'\nimport { installMobile } from './mobile.js'\n\nexport const inject = ['connection', 'workspaces', 'slots', 'layout', 'conversation']\n\nconst SECTION_ID = 'dshb-nodes'\n\ninterface NodeProfileView {\n id: string\n name: string\n type: 'local-host' | 'local-docker' | 'remote-ssh' | 'remote-docker'\n ssh?: {\n host: string\n port: number\n username: string\n auth: { kind: 'password' | 'key' | 'agent'; keyPath?: string }\n }\n docker?: {\n image?: string\n containerId?: string\n mode?: string\n resources?: { cpus?: number; memoryMB?: number }\n }\n hasSecret: { hasPassword: boolean; hasKey: boolean; hasPassphrase: boolean }\n status?: { reachable?: boolean; lastCheckedAt?: string; error?: string }\n provision?: { state: 'provisioning' | 'ready' | 'failed'; error?: string; containerId?: string; updatedAt: string }\n}\n\ninterface SshConfigEntryView {\n host: string\n port: number\n username: string\n identityFile?: string\n hasIdentityFile: boolean\n hasProxyJump: boolean\n}\n\ninterface FormState {\n name: string\n type: 'local-host' | 'local-docker' | 'remote-ssh' | 'remote-docker'\n host: string\n port: string\n username: string\n authKind: 'password' | 'key' | 'agent'\n password: string\n privateKey: string\n passphrase: string\n keyPath: string\n dockerImage: string\n dockerCpus: string\n dockerMemory: string\n}\n\nconst EMPTY_FORM: FormState = {\n name: '',\n type: 'remote-ssh',\n host: '',\n port: '22',\n username: '',\n authKind: 'password',\n password: '',\n privateKey: '',\n passphrase: '',\n keyPath: '',\n dockerImage: '',\n dockerCpus: '',\n dockerMemory: '',\n}\n\ntype Notice = { kind: 'ok' | 'error'; text: string } | undefined\n\nconst inputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n padding: '0 10px',\n height: 32,\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: 8,\n fontSize: 14,\n fontFamily: 'inherit',\n background: 'var(--dsw-alias-bg-layer-1)',\n color: 'var(--dsw-alias-label-primary)',\n outline: 'none',\n}\nconst buttonStyle: CSSProperties = {\n boxSizing: 'border-box',\n height: 36,\n padding: '0 14px',\n borderRadius: 18,\n fontSize: 14,\n lineHeight: '22px',\n fontFamily: 'inherit',\n cursor: 'pointer',\n border: 'none',\n display: 'inline-flex',\n justifyContent: 'center',\n alignItems: 'center',\n gap: 4,\n}\nconst primaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n background: 'var(--dsw-alias-button-primary-fill)',\n color: 'var(--dsw-alias-label-primary-foreground)',\n}\nconst secondaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-primary)',\n background: 'transparent',\n}\nconst dangerOutlineButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-state-error-primary)',\n color: 'var(--dsw-alias-state-error-primary)',\n background: 'transparent',\n}\n\nfunction useNodes(reloadKey: number): NodeProfileView[] {\n const [nodes, setNodes] = useState<NodeProfileView[]>([])\n useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await fetch('/api/dshb/nodes')\n if (!res.ok) return\n const data = (await res.json()) as { nodes?: NodeProfileView[] }\n if (!cancelled) setNodes(data.nodes ?? [])\n } catch {}\n })()\n return () => {\n cancelled = true\n }\n }, [reloadKey])\n return nodes\n}\n\nexport function NodeSection(_props: PropsRuntime<'settings.section'>): ReactElement {\n const [reloadKey, setReloadKey] = useState(0)\n const nodes = useNodes(reloadKey)\n const [selectedId, setSelectedId] = useState<string | 'new'>('new')\n const [form, setForm] = useState<FormState>(EMPTY_FORM)\n const [notice, setNotice] = useState<Notice>(undefined)\n const [busy, setBusy] = useState(false)\n const [testing, setTesting] = useState(false)\n const [testResult, setTestResult] = useState<string | undefined>(undefined)\n const [sshConfigEntries, setSshConfigEntries] = useState<SshConfigEntryView[]>([])\n const [dockerBusy, setDockerBusy] = useState(false)\n\n const flash = useCallback((n: Notice) => {\n setNotice(n)\n if (n) setTimeout(() => setNotice(undefined), 6000)\n }, [])\n\n const reload = useCallback(() => setReloadKey((k) => k + 1), [])\n\n useEffect(() => {\n void (async () => {\n try {\n const res = await fetch('/api/dshb/ssh-config')\n if (!res.ok) return\n const data = (await res.json()) as { entries?: SshConfigEntryView[] }\n setSshConfigEntries(data.entries ?? [])\n } catch {}\n })()\n }, [])\n\n const selectNode = useCallback(\n (id: string | 'new') => {\n setSelectedId(id)\n setTestResult(undefined)\n setTesting(false)\n setDockerBusy(false)\n if (id === 'new') {\n setForm(EMPTY_FORM)\n return\n }\n const node = nodes.find((n) => n.id === id)\n if (!node) return\n setForm({\n name: node.name,\n type: node.type,\n host: node.ssh?.host ?? '',\n port: String(node.ssh?.port ?? 22),\n username: node.ssh?.username ?? '',\n authKind: node.ssh?.auth.kind ?? 'password',\n password: '',\n privateKey: '',\n passphrase: '',\n keyPath: node.ssh?.auth.keyPath ?? '',\n dockerImage: node.docker?.image ?? '',\n dockerCpus: node.docker?.resources?.cpus != null ? String(node.docker?.resources?.cpus) : '',\n dockerMemory: node.docker?.resources?.memoryMB != null ? String(node.docker?.resources?.memoryMB) : '',\n })\n },\n [nodes],\n )\n\n const importSshConfig = useCallback((entry: SshConfigEntryView) => {\n setForm((f) => ({\n ...f,\n type: 'remote-ssh',\n host: entry.host,\n port: String(entry.port),\n username: entry.username,\n authKind: entry.hasIdentityFile ? 'key' : f.authKind,\n keyPath: entry.identityFile ?? f.keyPath,\n name: f.name || entry.host,\n }))\n setTestResult(undefined)\n flash({ kind: 'ok', text: `已从 ~/.ssh/config 导入 ${entry.host}` })\n }, [flash])\n\n const save = useCallback(async () => {\n if (!form.name.trim()) {\n flash({ kind: 'error', text: '请填写节点名称' })\n return\n }\n setBusy(true)\n try {\n const isRemote = form.type === 'remote-ssh' || form.type === 'remote-docker'\n const isDocker = form.type === 'local-docker' || form.type === 'remote-docker'\n const payload: Record<string, unknown> = {\n name: form.name.trim(),\n type: form.type,\n ssh: isRemote\n ? {\n host: form.host.trim(),\n port: Number(form.port) || 22,\n username: form.username.trim(),\n auth: { kind: form.authKind, ...(form.keyPath.trim() ? { keyPath: form.keyPath.trim() } : {}) },\n }\n : undefined,\n secrets: isRemote\n ? {\n ...(form.password ? { password: form.password } : {}),\n ...(form.privateKey ? { privateKey: form.privateKey } : {}),\n ...(form.passphrase ? { passphrase: form.passphrase } : {}),\n }\n : undefined,\n docker: isDocker\n ? {\n mode: 'managed' as const,\n ...(form.dockerImage.trim() ? { image: form.dockerImage.trim() } : {}),\n ...(form.dockerCpus ? { resources: { cpus: Number(form.dockerCpus) || undefined } } : {}),\n ...(form.dockerMemory ? { resources: { memoryMB: Number(form.dockerMemory) || undefined } } : {}),\n ...(form.dockerCpus && form.dockerMemory ? { resources: { cpus: Number(form.dockerCpus) || undefined, memoryMB: Number(form.dockerMemory) || undefined } } : {}),\n }\n : undefined,\n }\n const url = selectedId === 'new' ? '/api/dshb/nodes' : `/api/dshb/nodes/${selectedId}`\n const res = await fetch(url, {\n method: selectedId === 'new' ? 'POST' : 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const data = (await res.json()) as { ok?: boolean; error?: string; node?: NodeProfileView }\n if (res.ok && data.ok) {\n flash({ kind: 'ok', text: selectedId === 'new' ? '节点已创建' : '节点已保存' })\n reload()\n if (data.node) setSelectedId(data.node.id)\n } else {\n flash({ kind: 'error', text: data.error ?? '保存失败' })\n }\n } catch {\n flash({ kind: 'error', text: '网络错误' })\n } finally {\n setBusy(false)\n }\n }, [form, selectedId, flash, reload])\n\n const remove = useCallback(async () => {\n if (selectedId === 'new') return\n setBusy(true)\n try {\n // 容器类型节点:先删除关联容器(失败不阻断节点删除)\n const node = nodes.find((n) => n.id === selectedId)\n if (node && (node.type === 'local-docker' || node.type === 'remote-docker') && node.docker?.containerId) {\n try {\n await fetch(`/api/dshb/docker/${selectedId}/delete`, { method: 'POST' })\n } catch {\n // 容器删除失败仍继续删节点配置\n }\n }\n const res = await fetch(`/api/dshb/nodes/${selectedId}`, { method: 'DELETE' })\n if (res.ok) {\n flash({ kind: 'ok', text: '节点已删除' })\n setSelectedId('new')\n setForm(EMPTY_FORM)\n reload()\n }\n } catch {\n flash({ kind: 'error', text: '删除失败' })\n } finally {\n setBusy(false)\n }\n }, [selectedId, nodes, flash, reload])\n\n const test = useCallback(async () => {\n if (form.type === 'local-host') {\n flash({ kind: 'error', text: '本地节点无需测试连接' })\n return\n }\n const isRemote = form.type === 'remote-ssh' || form.type === 'remote-docker'\n if (isRemote && !form.host.trim()) {\n flash({ kind: 'error', text: '请填写主机地址' })\n return\n }\n setTesting(true)\n setTestResult(undefined)\n try {\n const payload = isRemote ? {\n ssh: {\n host: form.host.trim(),\n port: Number(form.port) || 22,\n username: form.username.trim(),\n auth: { kind: form.authKind, ...(form.keyPath.trim() ? { keyPath: form.keyPath.trim() } : {}) },\n },\n secrets: {\n ...(form.password ? { password: form.password } : {}),\n ...(form.privateKey ? { privateKey: form.privateKey } : {}),\n ...(form.passphrase ? { passphrase: form.passphrase } : {}),\n },\n } : {}\n const url = selectedId === 'new' ? '/api/dshb/nodes/test-unsaved' : `/api/dshb/nodes/${selectedId}/test`\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const data = (await res.json()) as { ok?: boolean; report?: { ok?: boolean; reachable?: boolean; error?: string; category?: string } }\n const r = data.report\n if (r?.ok && r.reachable) {\n setTestResult('连接成功')\n } else {\n const cat = r?.category ? `(${r.category})` : ''\n setTestResult(`${r?.error ?? '连接失败'}${cat}`)\n }\n reload()\n } catch {\n setTestResult('网络错误')\n } finally {\n setTesting(false)\n }\n }, [form, selectedId, reload])\n\n const dockerAction = useCallback(async (action: 'restart' | 'stop' | 'start' | 'provision') => {\n if (selectedId === 'new') return\n if (action === 'provision') {\n setDockerBusy(true)\n try {\n const res = await fetch(`/api/dshb/docker/${selectedId}/provision`, { method: 'POST' })\n const data = (await res.json()) as { ok?: boolean; error?: string; containerId?: string }\n if (res.ok && data.ok) {\n flash({ kind: 'ok', text: `容器已就绪:${data.containerId?.slice(0, 12) ?? ''}` })\n } else {\n flash({ kind: 'error', text: data.error ?? '拉起失败' })\n }\n reload()\n } catch {\n flash({ kind: 'error', text: '网络错误' })\n } finally {\n setDockerBusy(false)\n }\n return\n }\n setDockerBusy(true)\n try {\n const res = await fetch(`/api/dshb/docker/${selectedId}/${action}`, { method: 'POST' })\n const data = (await res.json()) as { ok?: boolean; error?: string }\n if (res.ok && data.ok) {\n flash({ kind: 'ok', text: action === 'restart' ? '容器已重启' : action === 'stop' ? '容器已停止' : '容器已启动' })\n } else {\n flash({ kind: 'error', text: data.error ?? '操作失败' })\n }\n reload()\n } catch {\n flash({ kind: 'error', text: '网络错误' })\n } finally {\n setDockerBusy(false)\n }\n }, [selectedId, flash, reload])\n\n const selectedNode = selectedId === 'new' ? undefined : nodes.find((n) => n.id === selectedId)\n\n return (\n <>\n <style>{`\n @media (max-width:767px){\n .dshb-node-section{flex-direction:column!important}\n .dshb-node-section>div:first-child{width:100%!important;border-right:none!important;border-bottom:1px solid var(--dsw-alias-border-l2);padding-right:0!important;padding-bottom:12px;margin-bottom:12px}\n .dshb-node-section>div:last-child{max-width:100%!important}\n .dshb-form-row{flex-direction:column!important;gap:8px!important}\n .dshb-form-row>label{width:100%!important;flex:none!important}\n }\n `}</style>\n <div className=\"dshb-node-section\" style={{ display: 'flex', gap: 20, minHeight: 320, overflow: 'hidden', maxWidth: '100%', width: '100%', boxSizing: 'border-box' }}>\n <div style={{ width: 200, flexShrink: 0, borderRight: '1px solid var(--dsw-alias-border-l2)', paddingRight: 16 }}>\n <button type=\"button\" onClick={() => selectNode('new')} style={{ ...primaryButtonStyle, width: '100%', marginBottom: 10 }}>\n 新建节点\n </button>\n {nodes.map((n) => (\n <div\n key={n.id}\n onClick={() => selectNode(n.id)}\n style={{\n padding: '8px 10px',\n borderRadius: 6,\n cursor: 'pointer',\n marginBottom: 4,\n background: selectedId === n.id ? 'var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover))' : 'transparent',\n fontSize: 13,\n }}\n >\n <div style={{ fontWeight: 500 }}>{n.name}</div>\n <div style={{ fontSize: 11, opacity: 0.7, display: 'flex', alignItems: 'center', gap: 4 }}>\n <span\n style={{\n width: 6,\n height: 6,\n borderRadius: '50%',\n background: n.status?.reachable === true ? 'var(--dsw-alias-state-success-primary)' : n.status?.reachable === false ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-label-dimmed)',\n }}\n />\n {n.type === 'local-host' ? '默认环境(本地)' : n.type === 'remote-ssh' ? '远程 SSH' : n.type === 'local-docker' ? '本地 Docker' : n.type === 'remote-docker' ? '远程 Docker' : n.type}\n <span style={{ marginLeft: 4, color: n.status?.reachable === true ? 'var(--dsw-alias-state-success-primary)' : n.status?.reachable === false ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-label-dimmed)' }}>\n {n.status?.reachable === true ? '在线' : n.status?.reachable === false ? '离线' : '未知'}\n </span>\n </div>\n {n.provision && (\n <div style={{ fontSize: 11, marginTop: 2, color: n.provision.state === 'ready' ? 'var(--dsw-alias-state-success-primary)' : n.provision.state === 'failed' ? 'var(--dsw-alias-state-error-primary)' : 'var(--dsw-alias-state-warn-label)' }}>\n {n.provision.state === 'provisioning' ? '容器拉起中…' : n.provision.state === 'ready' ? '容器已就绪' : `容器拉起失败:${n.provision.error ?? '未知错误'}`}\n </div>\n )}\n </div>\n ))}\n {nodes.length === 0 && <div style={{ fontSize: 12, opacity: 0.6 }}>暂无节点</div>}\n </div>\n\n <div style={{ flex: 1, maxWidth: 480, minWidth: 0, overflow: 'hidden' }}>\n {sshConfigEntries.length > 0 && (\n <div style={{ marginBottom: 14 }}>\n <label style={{ fontSize: 12, opacity: 0.75, display: 'block', marginBottom: 4 }}>从 ~/.ssh/config 导入</label>\n <select\n style={inputStyle}\n value=\"\"\n onChange={(e) => {\n const entry = sshConfigEntries[Number(e.target.value)]\n if (entry) importSshConfig(entry)\n e.target.value = ''\n }}\n >\n <option value=\"\">选择主机…</option>\n {sshConfigEntries.map((entry, i) => (\n <option key={entry.host} value={i}>\n {entry.host}:{entry.port}{entry.username ? ` (${entry.username})` : ''}{entry.hasProxyJump ? ' 跳板' : ''}\n </option>\n ))}\n </select>\n </div>\n )}\n\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 节点名称\n <input style={inputStyle} value={form.name} onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))} placeholder=\"例如:构建机\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 节点类型\n <select style={inputStyle} value={form.type} onChange={(e) => setForm((f) => ({ ...f, type: e.target.value as FormState['type'] }))}>\n <option value=\"remote-ssh\">远程 SSH 宿主机</option>\n <option value=\"remote-docker\">远程 Docker(SSH 通道)</option>\n <option value=\"local-host\">默认环境(本地)</option>\n <option value=\"local-docker\">本地 Docker 容器</option>\n </select>\n </label>\n\n {(form.type === 'remote-ssh' || form.type === 'remote-docker') && (\n <>\n <div className=\"dshb-form-row\" style={{ display: 'flex', gap: 10 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13, flex: 1, minWidth: 0 }}>\n 主机地址\n <input style={inputStyle} value={form.host} onChange={(e) => setForm((f) => ({ ...f, host: e.target.value }))} placeholder=\"192.168.1.10\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13, width: 90 }}>\n 端口\n <input style={inputStyle} value={form.port} onChange={(e) => setForm((f) => ({ ...f, port: e.target.value }))} />\n </label>\n </div>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 用户名\n <input style={inputStyle} value={form.username} onChange={(e) => setForm((f) => ({ ...f, username: e.target.value }))} placeholder=\"root\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 认证方式\n <select style={inputStyle} value={form.authKind} onChange={(e) => setForm((f) => ({ ...f, authKind: e.target.value as FormState['authKind'] }))}>\n <option value=\"password\">密码</option>\n <option value=\"key\">私钥</option>\n <option value=\"agent\">ssh-agent</option>\n </select>\n </label>\n {form.authKind === 'password' && (\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 密码{selectedNode?.hasSecret.hasPassword ? '(已保存,留空保持不变)' : ''}\n <input style={inputStyle} type=\"password\" value={form.password} onChange={(e) => setForm((f) => ({ ...f, password: e.target.value }))} autoComplete=\"new-password\" />\n </label>\n )}\n {form.authKind === 'key' && (\n <>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 私钥内容{selectedNode?.hasSecret.hasKey ? '(已保存,留空保持不变)' : ''}\n <textarea style={{ ...inputStyle, minHeight: 90, resize: 'vertical' }} value={form.privateKey} onChange={(e) => setForm((f) => ({ ...f, privateKey: e.target.value }))} placeholder=\"-----BEGIN OPENSSH PRIVATE KEY-----\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 私钥 passphrase(可选)\n <input style={inputStyle} type=\"password\" value={form.passphrase} onChange={(e) => setForm((f) => ({ ...f, passphrase: e.target.value }))} autoComplete=\"new-password\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 或私钥文件路径(可选,优先使用内容)\n <input style={inputStyle} value={form.keyPath} onChange={(e) => setForm((f) => ({ ...f, keyPath: e.target.value }))} placeholder=\"~/.ssh/id_ed25519\" />\n </label>\n </>\n )}\n </>\n )}\n\n {(form.type === 'local-docker' || form.type === 'remote-docker') && (\n <>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 镜像地址\n <input style={inputStyle} value={form.dockerImage} onChange={(e) => setForm((f) => ({ ...f, dockerImage: e.target.value }))} placeholder=\"alpine:latest\" />\n </label>\n <div className=\"dshb-form-row\" style={{ display: 'flex', gap: 10 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13, flex: 1, minWidth: 0 }}>\n CPU 核数(可选)\n <input style={inputStyle} value={form.dockerCpus} onChange={(e) => setForm((f) => ({ ...f, dockerCpus: e.target.value }))} placeholder=\"2\" />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13, flex: 1, minWidth: 0 }}>\n 内存 MB(可选)\n <input style={inputStyle} value={form.dockerMemory} onChange={(e) => setForm((f) => ({ ...f, dockerMemory: e.target.value }))} placeholder=\"512\" />\n </label>\n </div>\n </>\n )}\n\n {testResult !== undefined && (\n <div style={{ fontSize: 13, color: testResult === '连接成功' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{testResult}</div>\n )}\n\n <div style={{ display: 'flex', gap: 10, marginTop: 6, flexWrap: 'wrap' }}>\n <button type=\"button\" onClick={() => void save()} disabled={busy} style={primaryButtonStyle}>\n {selectedId === 'new' ? '创建节点' : '保存'}\n </button>\n {(form.type === 'remote-ssh' || form.type === 'remote-docker' || (selectedId !== 'new' && form.type === 'local-docker')) && (\n <button type=\"button\" onClick={() => void test()} disabled={busy || testing} style={secondaryButtonStyle}>\n {testing ? '测试中…' : '测试连接'}\n </button>\n )}\n {selectedId !== 'new' && (form.type === 'local-docker' || form.type === 'remote-docker') && (\n <>\n <button type=\"button\" onClick={() => void dockerAction('provision')} disabled={busy || dockerBusy} style={primaryButtonStyle}>\n {dockerBusy ? '操作中…' : '拉起容器'}\n </button>\n <button type=\"button\" onClick={() => void dockerAction('restart')} disabled={busy || dockerBusy} style={secondaryButtonStyle}>\n {dockerBusy ? '操作中…' : '重启容器'}\n </button>\n <button type=\"button\" onClick={() => void dockerAction('stop')} disabled={busy || dockerBusy} style={secondaryButtonStyle}>\n {dockerBusy ? '操作中…' : '停止容器'}\n </button>\n </>\n )}\n {selectedId !== 'new' && (\n <button type=\"button\" onClick={() => void remove()} disabled={busy} style={dangerOutlineButtonStyle}>\n 删除\n </button>\n )}\n {notice && <span style={{ fontSize: 13, alignSelf: 'center', color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{notice.text}</span>}\n </div>\n </div>\n </div>\n </div>\n </>\n )\n}\n\nexport function apply(ctx: ClientContext): void {\n const globalStyle = document.createElement('style')\n globalStyle.textContent = `\n /* 仅在页面根阻止整页横向滚动。绝对定位浮层(模型菜单、上下文统计面板等)\n 依赖祖先 overflow:visible 才能展开到输入框之外,禁止给任何中间祖先加裁剪。 */\n html, body { overflow-x: hidden !important; max-width: 100vw !important }\n * { min-width: 0 !important; }\n table { display: block !important; overflow-x: auto !important; }\n img, video, canvas, svg { max-width: 100% !important; height: auto !important }\n pre, code { white-space: pre-wrap !important; word-break: break-all !important }\n input, select, textarea { max-width: 100% !important; }\n /* 上下文用量弹窗 .JObwrW_panel(ContextMeter):解除宽度塌缩并允许溢出显示。\n JObwrW_ 为上游 CSS 模块 hash,上游升级需同步。 */\n .JObwrW_panel { max-width: none !important; overflow: visible !important; }\n /* 子代理切换弹窗(dsh-client-ui-subagent):menu 是 flex column + max-height + overflow:auto,\n position:fixed 经 portal 渲染。子代理很多时 flex 压缩 node(flex-shrink:1 默认),\n 文字挤压且不触发滚动。给 node flex-shrink:0 让 menu 整体 overflow:auto 滚动。 */\n [class*=\"_menu\"] [class*=\"_node\"] { flex-shrink: 0 !important; }\n /* 模型选择弹窗(dsh-client-ui-model-selection):Ra_menu 上游 max-width 420px,\n 长模型名单行放不下被 ellipsis 截断。放宽到 720px 让模型名单行完整显示。\n [class*=\"Ra_menu\"] 精确匹配模型弹窗,避免误伤其他菜单。Ra_ 为 hash,上游升级需同步。 */\n [class*=\"Ra_menu\"] { max-width: 720px !important; width: auto !important; }\n /* 终端组件(dsh-web-frontend,CSS 模块前缀 1gdtu_)桌面端修复:上游 _command_ 默认\n overflow:hidden+text-overflow:ellipsis,LLM 调 bash 的长命令在桌面端被省略号\n 截断、看不到完整内容(输出区 _output_ 本就可横向滚动,唯命令区不行)。宽屏与\n mobile.tsx 的窄屏规则等效:行链 _prompt_/_promptLine_/_command_ 撑到 max-content、\n overflow 可见,长行溢出传入 _header_(本就 overflow-y:auto)的 scrollWidth,整个\n 命令区随 _header_ 横向滚动;copyButton 贴右缘保持可见。与窄屏规则共用 1gdtu_\n hash,上游升级需同步。桌面保留可见滚动条(不隐藏、不加 touch-action)。 */\n @media (min-width: 1024px) {\n [data-phase] [class*=\"_header_1gdtu_\"] {\n min-width: 0 !important;\n max-width: 100% !important;\n overflow-x: auto !important;\n }\n [data-phase] [class*=\"_prompt_1gdtu_\"],\n [data-phase] [class*=\"_promptLine_1gdtu_\"],\n [data-phase] [class*=\"_command_1gdtu_\"] {\n min-width: max-content !important;\n max-width: none !important;\n overflow: visible !important;\n }\n [data-phase] [class*=\"_command_1gdtu_\"] {\n display: inline-block;\n text-overflow: clip !important;\n white-space: pre !important;\n }\n [data-phase] [class*=\"_copyButton_1gdtu_\"] { position: sticky !important; right: 0; top: 0; }\n }\n `\n document.head.appendChild(globalStyle)\n\n installMobile(ctx)\n\n ctx.plugin({\n inject: ['slots', 'settingsScope'],\n apply: (sub: ClientContext): void => {\n sub.slots.inject('settings.section', () =>\n sub.slots.register({ name: 'settings.section', id: SECTION_ID, order: 110, label: () => '工作节点' }, NodeSection),\n )\n /* 上游 dsh-client-ui-directory-picker-browse 以默认 priority 0 注册同一 slot;\n priority 升序遮蔽(lowest renders),用 -10 抢占渲染,避免同优先级抛错。 */\n sub.slots.inject('conversation.hero.workspace.directoryFlow', () =>\n sub.slots.register({ name: 'conversation.hero.workspace.directoryFlow', priority: -10 }, DirectoryFlowOccupant),\n )\n sub.slots.inject('sidebar.workspaces.directoryFlow', () =>\n sub.slots.register({ name: 'sidebar.workspaces.directoryFlow', priority: -10 }, DirectoryFlowOccupant),\n )\n },\n })\n\n const workspaces = (ctx as unknown as { workspaces?: { openPath(p: string): Promise<void> } }).workspaces\n if (workspaces && typeof workspaces.openPath === 'function') {\n const originalOpenPath = workspaces.openPath.bind(workspaces)\n workspaces.openPath = async (path: string): Promise<void> => {\n try {\n const res = await fetch(`/api/dshb/remote/bound?path=${encodeURIComponent(path)}`)\n if (res.ok) {\n const data = (await res.json()) as { bound?: boolean; kind?: 'file' | 'dir'; error?: string }\n if (data.bound) {\n if (data.kind !== 'file') return originalOpenPath(path)\n const a = document.createElement('a')\n a.href = `/api/dshb/remote/file?path=${encodeURIComponent(path)}`\n a.download = ''\n document.body.appendChild(a)\n a.click()\n a.remove()\n return\n }\n }\n } catch {\n // 探测失败则回退宿主原生打开\n }\n return originalOpenPath(path)\n }\n }\n\n const ICON_SVG: Record<string, string> = {\n auth: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 7C9.3807 7 10.5 5.8807 10.5 4.5C10.5 3.1193 9.3807 2 8 2C6.6193 2 5.5 3.1193 5.5 4.5C5.5 5.8807 6.6193 7 8 7Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/><path d=\"M3 14C3 11.2386 5.2386 9 8 9C10.7614 9 13 11.2386 13 14\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`,\n 'dshb-nodes': `<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"3.5\" cy=\"4\" r=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"12.5\" cy=\"4\" r=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/><circle cx=\"8\" cy=\"12\" r=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M3.5 6V8C3.5 9.1 4.4 10 5.5 10H8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/><path d=\"M12.5 6V8C12.5 9.1 11.6 10 10.5 10H8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>`,\n }\n const ICON_LABELS: Record<string, string> = { '认证': 'auth', '工作节点': 'dshb-nodes' }\n\n const observer = new MutationObserver(() => {\n const cells = document.querySelectorAll('button[class*=\"navCell\"]')\n for (const cell of Array.from(cells)) {\n const label = cell.querySelector('span[class*=\"navLabel\"]')\n if (!label) continue\n const text = label.textContent ?? ''\n const iconKey = ICON_LABELS[text]\n if (!iconKey) continue\n const iconSlot = cell.querySelector('[class*=\"navIcon\"]')\n if (!iconSlot) continue\n if (iconSlot.getAttribute('data-dshb-icon') === iconKey) continue\n iconSlot.innerHTML = ICON_SVG[iconKey] ?? ''\n iconSlot.setAttribute('data-dshb-icon', iconKey)\n }\n })\n observer.observe(document.body, { childList: true, subtree: true })\n}\n","import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport { apply as applyAuth } from './auth.js'\nimport { apply as applyUi } from './ui.js'\n\nexport const inject = ['connection', 'workspaces', 'slots', 'layout', 'conversation']\n\nexport function apply(ctx: ClientContext): void {\n applyAuth(ctx)\n applyUi(ctx)\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { Client } from "ssh2";
2
+ //#region src/exec-ssh/connection.d.ts
3
+ interface SshAuthConfig {
4
+ kind: 'password' | 'key' | 'agent';
5
+ keyPath?: string;
6
+ }
7
+ interface SshJumpHop {
8
+ host: string;
9
+ port?: number;
10
+ username?: string;
11
+ keyPath?: string;
12
+ }
13
+ interface SshTarget {
14
+ host: string;
15
+ port: number;
16
+ username: string;
17
+ auth: SshAuthConfig;
18
+ jump?: SshJumpHop[];
19
+ }
20
+ interface SshSecrets {
21
+ password?: string;
22
+ privateKey?: string;
23
+ passphrase?: string;
24
+ }
25
+ type SshErrorCategory = 'auth' | 'network' | 'hostkey' | 'timeout' | 'unknown';
26
+ interface SshConnectionError extends Error {
27
+ category: SshErrorCategory;
28
+ cause?: unknown;
29
+ }
30
+ declare function classifySshError(err: unknown): SshErrorCategory;
31
+ declare function toConnectionError(err: unknown): SshConnectionError;
32
+ declare class SshConnection {
33
+ private readonly target;
34
+ private readonly secrets;
35
+ private client;
36
+ private connecting;
37
+ private closed;
38
+ constructor(target: SshTarget, secrets: SshSecrets);
39
+ getClient(): Promise<Client>;
40
+ private connectWithRetry;
41
+ private connectOnce;
42
+ close(): void;
43
+ }
44
+ //#endregion
45
+ export { SshJumpHop as a, classifySshError as c, SshErrorCategory as i, toConnectionError as l, SshConnection as n, SshSecrets as o, SshConnectionError as r, SshTarget as s, SshAuthConfig as t };
46
+ //# sourceMappingURL=connection-sIX25pkS.d.ts.map
@@ -0,0 +1,16 @@
1
+ //#region src/core/audit.d.ts
2
+ interface AuditEntry {
3
+ time: string;
4
+ nodeId: string;
5
+ op: 'exec' | 'write' | 'remove' | 'move' | 'provision' | 'connect';
6
+ target: string;
7
+ result: string;
8
+ }
9
+ declare class AuditLogger {
10
+ private file;
11
+ log(nodeId: string, op: AuditEntry['op'], target: string, result: string): void;
12
+ }
13
+ declare function sharedAuditLogger(): AuditLogger;
14
+ //#endregion
15
+ export { AuditEntry, AuditLogger, sharedAuditLogger };
16
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { homedir } from "node:os";
2
+ import { appendFileSync, mkdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ //#region src/core/audit.ts
5
+ function dshHome() {
6
+ return process.env.DSH_HOME ?? join(homedir(), ".dsh");
7
+ }
8
+ var AuditLogger = class {
9
+ file() {
10
+ return join(dshHome(), "dshb", "audit.log");
11
+ }
12
+ log(nodeId, op, target, result) {
13
+ try {
14
+ const file = this.file();
15
+ mkdirSync(join(file, ".."), {
16
+ recursive: true,
17
+ mode: 448
18
+ });
19
+ const entry = {
20
+ time: (/* @__PURE__ */ new Date()).toISOString(),
21
+ nodeId,
22
+ op,
23
+ target,
24
+ result
25
+ };
26
+ appendFileSync(file, `${JSON.stringify(entry)}\n`, { mode: 384 });
27
+ } catch {}
28
+ }
29
+ };
30
+ let shared = null;
31
+ function sharedAuditLogger() {
32
+ if (!shared) shared = new AuditLogger();
33
+ return shared;
34
+ }
35
+ //#endregion
36
+ export { AuditLogger, sharedAuditLogger };
37
+
38
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","names":[],"sources":["../../src/core/audit.ts"],"sourcesContent":["import { appendFileSync, mkdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface AuditEntry {\n time: string\n nodeId: string\n op: 'exec' | 'write' | 'remove' | 'move' | 'provision' | 'connect'\n target: string\n result: string\n}\n\nfunction dshHome(): string {\n return process.env.DSH_HOME ?? join(homedir(), '.dsh')\n}\n\nexport class AuditLogger {\n private file(): string {\n return join(dshHome(), 'dshb', 'audit.log')\n }\n\n log(nodeId: string, op: AuditEntry['op'], target: string, result: string): void {\n try {\n const file = this.file()\n mkdirSync(join(file, '..'), { recursive: true, mode: 0o700 })\n const entry: AuditEntry = { time: new Date().toISOString(), nodeId, op, target, result }\n appendFileSync(file, `${JSON.stringify(entry)}\\n`, { mode: 0o600 })\n } catch {}\n }\n}\n\nlet shared: AuditLogger | null = null\n\nexport function sharedAuditLogger(): AuditLogger {\n if (!shared) shared = new AuditLogger()\n return shared\n}\n"],"mappings":";;;;AAYA,SAAS,UAAkB;CACzB,OAAO,QAAQ,IAAI,YAAY,KAAK,QAAQ,GAAG,MAAM;AACvD;AAEA,IAAa,cAAb,MAAyB;CACvB,OAAuB;EACrB,OAAO,KAAK,QAAQ,GAAG,QAAQ,WAAW;CAC5C;CAEA,IAAI,QAAgB,IAAsB,QAAgB,QAAsB;EAC9E,IAAI;GACF,MAAM,OAAO,KAAK,KAAK;GACvB,UAAU,KAAK,MAAM,IAAI,GAAG;IAAE,WAAW;IAAM,MAAM;GAAM,CAAC;GAC5D,MAAM,QAAoB;IAAE,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;IAAG;IAAQ;IAAI;IAAQ;GAAO;GACvF,eAAe,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;EACpE,QAAQ,CAAC;CACX;AACF;AAEA,IAAI,SAA6B;AAEjC,SAAgB,oBAAiC;CAC/C,IAAI,CAAC,QAAQ,SAAS,IAAI,YAAY;CACtC,OAAO;AACT"}
@@ -0,0 +1,8 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ //#region src/core/index.d.ts
3
+ declare const name = "dshb-core";
4
+ declare const inject: string[];
5
+ declare function apply(ctx: Context): void;
6
+ //#endregion
7
+ export { apply, inject, name };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,77 @@
1
+ import { sharedWorldResolver } from "../router/resolve.js";
2
+ import { sharedAuditLogger } from "./audit.js";
3
+ import { KnownHostsStore } from "./known-hosts.js";
4
+ import { NodeRegistry } from "./node-registry.js";
5
+ import { testNode } from "./test.js";
6
+ import { registerNodeRoutes, registerSshConfigRoutes } from "./routes.js";
7
+ import { registerRemoteDownloadRoutes, registerWorkspaceRoutes } from "./workspaces.js";
8
+ import { WorkspaceBindingsStore } from "./workspace-bindings.js";
9
+ //#region src/core/index.ts
10
+ const name = "dshb-core";
11
+ const inject = ["webServer", "credentials"];
12
+ function apply(ctx) {
13
+ const knownHosts = new KnownHostsStore();
14
+ const registry = new NodeRegistry(ctx);
15
+ const bindings = new WorkspaceBindingsStore();
16
+ ctx.provide("nodeRegistry", registry);
17
+ ctx.provide("knownHosts", knownHosts);
18
+ ctx.provide("workspaceBindings", bindings);
19
+ ctx.provide("dshbAudit", sharedAuditLogger());
20
+ sharedWorldResolver().setBindings(bindings);
21
+ registerNodeRoutes(ctx, registry);
22
+ registerSshConfigRoutes(ctx, knownHosts);
23
+ registerWorkspaceRoutes(ctx, registry, bindings);
24
+ registerRemoteDownloadRoutes(ctx, registry, bindings);
25
+ warmupWorlds(ctx, registry, bindings);
26
+ startHeartbeat(ctx, registry);
27
+ reprovisionDockerNodes(ctx, registry);
28
+ }
29
+ const HEARTBEAT_MS = 3e5;
30
+ function startHeartbeat(ctx, registry) {
31
+ const tick = async () => {
32
+ const sshTester = ctx.get("dshbSshPool");
33
+ const dockerTester = ctx.get("dshbDockerTester");
34
+ for (const node of registry.list()) try {
35
+ await testNode(registry, node.id, sshTester, void 0, dockerTester);
36
+ } catch {}
37
+ };
38
+ const timer = setInterval(() => void tick(), HEARTBEAT_MS);
39
+ ctx.effect(() => () => clearInterval(timer));
40
+ setImmediate(() => void tick());
41
+ }
42
+ function reprovisionDockerNodes(ctx, registry) {
43
+ setImmediate(() => {
44
+ (async () => {
45
+ const provisioner = ctx.get("dshbDockerProvisioner");
46
+ if (!provisioner) return;
47
+ for (const node of registry.list()) {
48
+ if (node.type !== "local-docker" && node.type !== "remote-docker") continue;
49
+ if (!node.docker?.image) continue;
50
+ if (node.docker?.containerId) continue;
51
+ try {
52
+ await provisioner.provision(node.id);
53
+ } catch {}
54
+ }
55
+ })();
56
+ });
57
+ }
58
+ function warmupWorlds(ctx, registry, bindings) {
59
+ setImmediate(() => {
60
+ (async () => {
61
+ for (const b of bindings.list()) {
62
+ const node = registry.get(b.nodeId);
63
+ if (!node || node.type === "local-host") continue;
64
+ try {
65
+ const worlds = node.type === "local-docker" || node.type === "remote-docker" ? ctx.get("dshbDockerWorldsGeneric") : ctx.get("dshbWorlds");
66
+ if (!worlds) continue;
67
+ const world = await worlds.ensure(b.nodeId);
68
+ if (world) await world.ensureDir(b.remotePath);
69
+ } catch {}
70
+ }
71
+ })();
72
+ });
73
+ }
74
+ //#endregion
75
+ export { apply, inject, name };
76
+
77
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/core/index.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis'\nimport { sharedWorldResolver } from '../router/resolve.js'\nimport type {} from '@deepseek-ai/dsh-host-webserver'\nimport { sharedAuditLogger } from './audit.js'\nimport { KnownHostsStore } from './known-hosts.js'\nimport { NodeRegistry } from './node-registry.js'\nimport { registerNodeRoutes, registerSshConfigRoutes } from './routes.js'\nimport { registerWorkspaceRoutes, registerRemoteDownloadRoutes } from './workspaces.js'\nimport { testNode, type DockerNodeTester, type SshHandshakeTester } from './test.js'\nimport { WorkspaceBindingsStore } from './workspace-bindings.js'\n\nexport const name = 'dshb-core'\n\nexport const inject = ['webServer', 'credentials']\n\nexport function apply(ctx: Context): void {\n const knownHosts = new KnownHostsStore()\n const registry = new NodeRegistry(ctx)\n const bindings = new WorkspaceBindingsStore()\n ctx.provide('nodeRegistry', registry)\n ctx.provide('knownHosts', knownHosts)\n ctx.provide('workspaceBindings', bindings)\n ctx.provide('dshbAudit', sharedAuditLogger())\n sharedWorldResolver().setBindings(bindings)\n void registerNodeRoutes(ctx, registry)\n void registerSshConfigRoutes(ctx, knownHosts)\n void registerWorkspaceRoutes(ctx, registry, bindings)\n void registerRemoteDownloadRoutes(ctx, registry, bindings)\n warmupWorlds(ctx, registry, bindings)\n startHeartbeat(ctx, registry)\n reprovisionDockerNodes(ctx, registry)\n}\n\nconst HEARTBEAT_MS = 5 * 60 * 1000\n\nfunction startHeartbeat(ctx: Context, registry: NodeRegistry): void {\n const tick = async (): Promise<void> => {\n const sshTester = ctx.get('dshbSshPool') as SshHandshakeTester | undefined\n const dockerTester = ctx.get('dshbDockerTester') as DockerNodeTester | undefined\n for (const node of registry.list()) {\n try {\n await testNode(registry, node.id, sshTester, undefined, dockerTester)\n } catch {\n // 单节点心跳失败不影响其他节点\n }\n }\n }\n const timer = setInterval(() => void tick(), HEARTBEAT_MS)\n ctx.effect(() => () => clearInterval(timer))\n setImmediate(() => void tick())\n}\n\nfunction reprovisionDockerNodes(ctx: Context, registry: NodeRegistry): void {\n setImmediate(() => {\n void (async () => {\n const provisioner = ctx.get('dshbDockerProvisioner') as { provision(nodeId: string): Promise<unknown> } | undefined\n if (!provisioner) return\n for (const node of registry.list()) {\n if (node.type !== 'local-docker' && node.type !== 'remote-docker') continue\n if (!node.docker?.image) continue\n if (node.docker?.containerId) continue\n try {\n await provisioner.provision(node.id)\n } catch {\n // 失败已记录到 provisionStatuses\n }\n }\n })()\n })\n}\n\ninterface WarmupWorldsLike {\n ensure(nodeId: string): Promise<{ ensureDir(path: string): Promise<void> } | undefined>\n}\n\nfunction warmupWorlds(ctx: Context, registry: NodeRegistry, bindings: WorkspaceBindingsStore): void {\n setImmediate(() => {\n void (async () => {\n for (const b of bindings.list()) {\n const node = registry.get(b.nodeId)\n if (!node || node.type === 'local-host') continue\n try {\n const isDocker = node.type === 'local-docker' || node.type === 'remote-docker'\n const worlds = isDocker\n ? (ctx.get('dshbDockerWorldsGeneric') as WarmupWorldsLike | undefined)\n : (ctx.get('dshbWorlds') as WarmupWorldsLike | undefined)\n if (!worlds) continue\n const world = await worlds.ensure(b.nodeId)\n if (world) await world.ensureDir(b.remotePath)\n } catch {\n // 预热失败不应阻塞启动;运行时可再次 ensure\n }\n }\n })()\n })\n}\n"],"mappings":";;;;;;;;;AAWA,MAAa,OAAO;AAEpB,MAAa,SAAS,CAAC,aAAa,aAAa;AAEjD,SAAgB,MAAM,KAAoB;CACxC,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,WAAW,IAAI,aAAa,GAAG;CACrC,MAAM,WAAW,IAAI,uBAAuB;CAC5C,IAAI,QAAQ,gBAAgB,QAAQ;CACpC,IAAI,QAAQ,cAAc,UAAU;CACpC,IAAI,QAAQ,qBAAqB,QAAQ;CACzC,IAAI,QAAQ,aAAa,kBAAkB,CAAC;CAC5C,oBAAoB,CAAC,CAAC,YAAY,QAAQ;CAC1C,mBAAwB,KAAK,QAAQ;CACrC,wBAA6B,KAAK,UAAU;CAC5C,wBAA6B,KAAK,UAAU,QAAQ;CACpD,6BAAkC,KAAK,UAAU,QAAQ;CACzD,aAAa,KAAK,UAAU,QAAQ;CACpC,eAAe,KAAK,QAAQ;CAC5B,uBAAuB,KAAK,QAAQ;AACtC;AAEA,MAAM,eAAe;AAErB,SAAS,eAAe,KAAc,UAA8B;CAClE,MAAM,OAAO,YAA2B;EACtC,MAAM,YAAY,IAAI,IAAI,aAAa;EACvC,MAAM,eAAe,IAAI,IAAI,kBAAkB;EAC/C,KAAK,MAAM,QAAQ,SAAS,KAAK,GAC/B,IAAI;GACF,MAAM,SAAS,UAAU,KAAK,IAAI,WAAW,KAAA,GAAW,YAAY;EACtE,QAAQ,CAER;CAEJ;CACA,MAAM,QAAQ,kBAAkB,KAAK,KAAK,GAAG,YAAY;CACzD,IAAI,mBAAmB,cAAc,KAAK,CAAC;CAC3C,mBAAmB,KAAK,KAAK,CAAC;AAChC;AAEA,SAAS,uBAAuB,KAAc,UAA8B;CAC1E,mBAAmB;EACjB,CAAM,YAAY;GAChB,MAAM,cAAc,IAAI,IAAI,uBAAuB;GACnD,IAAI,CAAC,aAAa;GAClB,KAAK,MAAM,QAAQ,SAAS,KAAK,GAAG;IAClC,IAAI,KAAK,SAAS,kBAAkB,KAAK,SAAS,iBAAiB;IACnE,IAAI,CAAC,KAAK,QAAQ,OAAO;IACzB,IAAI,KAAK,QAAQ,aAAa;IAC9B,IAAI;KACF,MAAM,YAAY,UAAU,KAAK,EAAE;IACrC,QAAQ,CAER;GACF;EACF,EAAA,CAAG;CACL,CAAC;AACH;AAMA,SAAS,aAAa,KAAc,UAAwB,UAAwC;CAClG,mBAAmB;EACjB,CAAM,YAAY;GAChB,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG;IAC/B,MAAM,OAAO,SAAS,IAAI,EAAE,MAAM;IAClC,IAAI,CAAC,QAAQ,KAAK,SAAS,cAAc;IACzC,IAAI;KAEF,MAAM,SADW,KAAK,SAAS,kBAAkB,KAAK,SAAS,kBAE1D,IAAI,IAAI,yBAAyB,IACjC,IAAI,IAAI,YAAY;KACzB,IAAI,CAAC,QAAQ;KACb,MAAM,QAAQ,MAAM,OAAO,OAAO,EAAE,MAAM;KAC1C,IAAI,OAAO,MAAM,MAAM,UAAU,EAAE,UAAU;IAC/C,QAAQ,CAER;GACF;EACF,EAAA,CAAG;CACL,CAAC;AACH"}
@@ -0,0 +1,23 @@
1
+ //#region src/core/known-hosts.d.ts
2
+ type HostKeyDecision = 'accept-new' | 'verify' | 'reject';
3
+ interface KnownHostEntry {
4
+ fingerprint: string;
5
+ recordedAt: string;
6
+ }
7
+ declare class KnownHostsStore {
8
+ private entries;
9
+ private loaded;
10
+ private file;
11
+ private load;
12
+ private save;
13
+ key(host: string, port: number): string;
14
+ get(host: string, port: number): KnownHostEntry | undefined;
15
+ record(host: string, port: number, fingerprint: string): void;
16
+ forget(host: string, port: number): void;
17
+ exists(): boolean;
18
+ check(host: string, port: number, fingerprint: string, mode?: HostKeyDecision): HostKeyDecision;
19
+ list(): Record<string, KnownHostEntry>;
20
+ }
21
+ //#endregion
22
+ export { HostKeyDecision, KnownHostEntry, KnownHostsStore };
23
+ //# sourceMappingURL=known-hosts.d.ts.map