device-center 0.1.0-beta.1 → 0.2.0-beta.2

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 (47) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/Dockerfile +2 -2
  3. package/README.md +37 -127
  4. package/SECURITY.md +7 -15
  5. package/bin/device-center.mjs +6 -3
  6. package/docs/agent-install.md +105 -0
  7. package/docs/architecture.md +25 -57
  8. package/docs/connection-routing.md +1 -0
  9. package/docs/device-profiles.md +56 -0
  10. package/docs/driver-binding.md +6 -1
  11. package/docs/friend-trial.md +42 -0
  12. package/docs/hosted-access.md +52 -0
  13. package/docs/npm.md +19 -35
  14. package/docs/onboarding.md +8 -5
  15. package/docs/remote-access.md +76 -0
  16. package/docs/roadmap.md +17 -28
  17. package/docs/security-and-sync.md +9 -64
  18. package/login.html +1 -1
  19. package/npm-shrinkwrap.json +304 -0
  20. package/package.json +22 -4
  21. package/scripts/access.mjs +48 -0
  22. package/scripts/driver.mjs +4 -3
  23. package/scripts/release-check.mjs +4 -0
  24. package/server/cloudAccess.mjs +13 -6
  25. package/server/connector.mjs +11 -5
  26. package/server/drivers/access.mjs +84 -0
  27. package/server/drivers/certificates.mjs +32 -0
  28. package/server/drivers/cloudLink.mjs +46 -4
  29. package/server/drivers/cloudRegistry.mjs +107 -20
  30. package/server/drivers/deviceProfiles.mjs +47 -0
  31. package/server/drivers/driverProtocol.mjs +3 -2
  32. package/server/drivers/inventory.mjs +21 -2
  33. package/server/drivers/relay.mjs +59 -0
  34. package/server/drivers/remote.mjs +111 -0
  35. package/server/index.mjs +44 -13
  36. package/server/mcp.mjs +5 -3
  37. package/server/unifiedAccess.mjs +33 -17
  38. package/src/api.js +4 -1
  39. package/src/device-state.js +19 -0
  40. package/src/login.css +4 -0
  41. package/src/login.js +1 -0
  42. package/src/main.js +197 -42
  43. package/src/onboarding.js +62 -0
  44. package/src/start.css +47 -0
  45. package/src/start.js +23 -0
  46. package/src/styles.css +22 -0
  47. package/start.html +45 -0
package/src/start.css ADDED
@@ -0,0 +1,47 @@
1
+ :root { color-scheme: light; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #25392d; background: #f5f7f2; }
2
+ * { box-sizing: border-box; }
3
+ body { margin: 0; }
4
+ a { color: #2f6d4c; text-underline-offset: 3px; }
5
+ header, main, footer { width: min(860px, calc(100% - 40px)); margin: 0 auto; }
6
+ header { display: flex; justify-content: space-between; align-items: center; padding: 24px 0; gap: 16px; font-size: 13px; }
7
+ .brand { display: flex; align-items: center; gap: 10px; font-weight: 700; text-decoration: none; }
8
+ .brand span { color: #25392d; }
9
+ .hero { padding: 48px 0 38px; }
10
+ .eyebrow { font-size: 12px; color: #70816d; letter-spacing: .8px; }
11
+ h1 { margin: 18px 0 20px; font-size: clamp(34px, 6vw, 54px); line-height: 1.2; letter-spacing: -1.8px; }
12
+ .lead { max-width: 630px; font-size: 17px; line-height: 1.8; color: #62705e; }
13
+ .platform { font-size: 12px; color: #70816d; }
14
+ section:not(.hero) { padding: 26px 0; border-top: 1px solid #dae3d6; }
15
+ h2 { margin: 0 0 22px; font-size: 19px; line-height: 1.6; }
16
+ .step { display: inline-grid; place-items: center; width: 28px; height: 28px; margin-right: 7px; border-radius: 50%; background: #e3eddd; font-size: 13px; }
17
+ .choices, .checks { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
18
+ article { display: flex; flex-direction: column; padding: 22px; border: 1px solid #dce5d7; border-radius: 12px; background: #fff; }
19
+ h3 { margin: 0 0 10px; font-size: 16px; }
20
+ p, li { font-size: 14px; line-height: 1.85; color: #62705e; }
21
+ article p { flex: 1; margin: 0 0 20px; }
22
+ button { min-height: 42px; border: 1px solid #c9d8c4; border-radius: 8px; padding: 10px 14px; color: #2f6d4c; background: #f4f8ef; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
23
+ button.primary { color: white; background: #356c4c; border-color: #356c4c; }
24
+ button:disabled { opacity: .6; }
25
+ button:hover { filter: brightness(.96); }
26
+ :focus-visible { outline: 2px solid #5c965d; outline-offset: 3px; }
27
+ .hint { font-size: 12px; }
28
+ details { margin-top: 18px; font-size: 13px; }
29
+ summary { color: #436440; cursor: pointer; line-height: 1.8; }
30
+ .custom label { display: block; margin: 14px 0 8px; }
31
+ .custom-row { display: flex; gap: 10px; }
32
+ input, textarea { width: 100%; min-width: 0; border: 1px solid #cbd7c5; border-radius: 8px; padding: 10px; background: white; color: #35482f; font: inherit; }
33
+ textarea { margin-top: 12px; font-family: ui-monospace, monospace; font-size: 12px; line-height: 1.7; resize: vertical; }
34
+ .custom-row button { flex-shrink: 0; }
35
+ #copy-result { min-height: 24px; margin: 12px 0; font-size: 13px; }
36
+ .success { color: #2f6d4c; }
37
+ .error { color: #9e3f32; }
38
+ ol { padding-left: 24px; }
39
+ li { padding: 0 0 12px 5px; }
40
+ .checks div { padding: 10px 0; }
41
+ .checks p { margin: 0; }
42
+ code { overflow-wrap: anywhere; font-size: 12px; }
43
+ aside { margin: 10px 0 32px; padding: 20px 22px; border: 1px solid #dce5d7; border-radius: 10px; background: #edf2e8; font-size: 14px; }
44
+ aside p { margin: 10px 0 0; font-size: 12px; }
45
+ footer { display: flex; flex-wrap: wrap; gap: 14px 22px; padding: 14px 0 32px; font-size: 12px; }
46
+ footer span { width: 100%; color: #7b8875; }
47
+ @media (max-width: 560px) { .choices, .checks { grid-template-columns: 1fr; gap: 12px; } .hero { padding-top: 25px; } .custom-row { flex-direction: column; } header { font-size: 12px; } h2 { font-size: 17px; } }
package/src/start.js ADDED
@@ -0,0 +1,23 @@
1
+ import { agentInstallPrompt } from './onboarding.js';
2
+
3
+ const text = document.querySelector('#install-instructions');
4
+ const result = document.querySelector('#copy-result');
5
+ text.value = agentInstallPrompt();
6
+ document.addEventListener('click', async event => {
7
+ const button = event.target.closest('button[data-mode]');
8
+ if (!button || button.disabled) return;
9
+ const mode = button.dataset.mode;
10
+ try {
11
+ text.value = agentInstallPrompt({ mode, ...(mode === 'self-hosted' ? { origin: document.querySelector('#custom-origin').value.trim() } : {}) });
12
+ } catch (error) { text.value = ''; result.textContent = error.message; result.className = 'error'; return; }
13
+ button.disabled = true;
14
+ let copied = false;
15
+ try { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(text.value); copied = true; } } catch { /* Keep manual copy available. */ }
16
+ if (!copied) {
17
+ document.querySelector('#instruction-details').open = true; text.focus(); text.select();
18
+ try { copied = document.execCommand('copy'); } catch { /* Selection is the fallback. */ }
19
+ }
20
+ result.className = copied ? 'success' : '';
21
+ result.textContent = copied ? '已复制。粘贴给本机 Agent;托管绑定时由你登录并核对。' : '请手动复制下方已选中的完整说明。';
22
+ button.disabled = false;
23
+ });
package/src/styles.css CHANGED
@@ -35,6 +35,13 @@ h1 { margin: 0 0 4px; font-size: 24px; letter-spacing: -.6px; font-weight: 680;
35
35
  .button-primary:hover { background: #1b6444; }
36
36
  .button-light { color: #607167; border-color: #dfe7df; background: #fff; }
37
37
  .button-light:hover { background: #f6f9f4; }
38
+ .heading-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; min-width: 0; }
39
+ .install-flow { margin: 14px 0; padding-left: 20px; color: #657660; font-size: 12px; line-height: 1.9; }
40
+ #agent-install-text { max-height: 170px; resize: vertical; margin-top: 10px; font-size: 11px; line-height: 1.7; color: #51644d; }
41
+ .install-copy { width: 100%; margin-top: 2px; }
42
+ .install-details { margin-top: 14px; color: #657660; font-size: 12px; }
43
+ #install-origin { width: 100%; border: 1px solid #dce4d8; padding: 9px 10px; border-radius: 7px; font: inherit; font-size: 12px; }
44
+ .install-mode-tabs { margin-bottom: 12px; }
38
45
  .text-button { border: 0; background: none; color: #688a73; }
39
46
  .icon-button { width: 32px; height: 32px; padding: 0; display: grid; place-items: center; border: 1px solid var(--line); background: #fff; border-radius: 8px; color: #839486; flex: 0 0 auto; }
40
47
  .connection-card { background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: 20px; box-shadow: 0 8px 26px #20352405; }
@@ -67,6 +74,19 @@ h1 { margin: 0 0 4px; font-size: 24px; letter-spacing: -.6px; font-weight: 680;
67
74
  .device-card { flex: 0 0 auto; display: grid; grid-template-columns: minmax(200px, 1.1fr) minmax(220px, 1fr) 130px; align-items: center; gap: 16px; background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; }
68
75
  .device-identity { min-width: 0; }
69
76
  .device-actions { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
77
+ .device-description { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin: 7px 0 0 33px; min-width: 0; }
78
+ .device-type, .device-tag { border-radius: 4px; padding: 2px 5px; font-size: 10px; color: #59675c; background: #f1f4ef; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
79
+ .device-type { color: #386842; background: #e9f3e6; }
80
+ .device-description p { flex-basis: 100%; margin: 2px 0 0; color: #69736a; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
81
+ .collection-hint { font-size: 10px; color: #7b8178; line-height: 1.4; }
82
+ .profile-edit { align-self: flex-start; font-size: 11px; }
83
+ .profile-fields { display: grid; grid-template-columns: minmax(0, 2fr) minmax(105px, 1fr); gap: 12px; }
84
+ #device-profile-form .field-label { display: block; margin: 14px 0 0; }
85
+ #device-profile-form input, #device-profile-form select, #device-profile-form textarea { display: block; width: 100%; margin: 7px 0 0; border: 1px solid #dce4d8; border-radius: 7px; padding: 9px 10px; font: inherit; font-size: 12px; background: white; color: #344137; }
86
+ #device-profile-form textarea { resize: vertical; min-height: 64px; }
87
+ .profile-identity { margin: 16px 0; font-size: 11px; color: #6a7569; }
88
+ .profile-identity summary { cursor: pointer; }
89
+ .save-profile { margin-top: 5px; width: 100%; }
70
90
  .driver-summary { grid-row: 2; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 6px 0; }
71
91
  .driver-summary strong { font-size: 11px; color: #407553; }
72
92
  .driver-summary p { margin: 0; font-size: 11px; color: #6d7d70; line-height: 1.5; }
@@ -221,6 +241,8 @@ details p { font-size: 12px; color: #8da37c; line-height: 1.8; }
221
241
  .status-help p { position: absolute; z-index: 3; right: 0; bottom: calc(100% + 8px); width: min(300px, calc(100vw - 28px)); max-height: 50dvh; overflow: auto; padding: 14px; margin: 0; font-size: 11px; color: #63765d; background: #fff; border: 1px solid var(--line); border-radius: 9px; box-shadow: 0 8px 30px #20352416; }
222
242
  @media (max-width: 960px) { .device-card { grid-template-columns: minmax(0, 1fr) 128px; gap: 9px 16px; } .device-identity { grid-column: 1 / -1; } }
223
243
  @media (max-width: 760px) {
244
+ .heading-actions { max-width: 140px; gap: 5px; }
245
+ .heading-actions > button { min-height: 30px; padding: 6px 8px; font-size: 11px; }
224
246
  .app-shell { grid-template-columns: 140px minmax(0, 1fr); }
225
247
  .sidebar { padding: 18px 10px; }
226
248
  .brand { gap: 7px; } .brand-mark { width: 28px; height: 28px; } .brand strong { font-size: 11px; } .brand small { font-size: 9px; }
package/start.html ADDED
@@ -0,0 +1,45 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="description" content="把自己的闲置电脑接入本机智能体。复制安装说明交给 Agent,选择本机使用、官方托管或自建服务。">
7
+ <title>开始试用 · Device Center</title>
8
+ <link rel="stylesheet" href="/src/start.css">
9
+ </head>
10
+ <body>
11
+ <header><a class="brand" href="/start">DC <span>Device Center</span></a><a href="/login">进入设备面板 ↗</a></header>
12
+ <main>
13
+ <section class="hero">
14
+ <p class="eyebrow">公开试用 · 0.2.0-beta.2 · MIT</p>
15
+ <h1>让你的 Agent<br>看见自己的闲置电脑。</h1>
16
+ <p class="lead">查看设备资源、记住每台电脑的用途,并在单独授权后读取指定日志。把安装交给本机 Agent,从一台电脑开始。</p>
17
+ <p class="platform">macOS / Linux · Node.js 24+ · Windows 原生版暂未支持</p>
18
+ </section>
19
+ <section aria-labelledby="install-title">
20
+ <h2 id="install-title"><span class="step">1</span> 选一种方式,复制给本机 Agent</h2>
21
+ <div class="choices">
22
+ <article><h3>本机使用</h3><p>本机面板和 MCP,无需账号。适合先了解自己的电脑与已有设备资源。</p><button data-mode="local">复制本机安装说明</button></article>
23
+ <article><h3>官方托管</h3><p>用自己的账号管理设备,使用官方加密中转。Driver 和私钥仍留在设备上。</p><button class="primary" data-mode="official">复制托管安装说明</button></article>
24
+ </div>
25
+ <p class="hint">粘贴给<strong>正在目标电脑上运行、能安装软件的 Agent</strong>。它会检查已有配置、安装固定版本、配置用户级后台服务及 MCP,再核验真实结果。网页本身不会安装软件。</p>
26
+ <details class="custom"><summary>已有自己的控制面?</summary><label for="custom-origin">自建控制面的 HTTPS 地址</label><div class="custom-row"><input id="custom-origin" type="url" placeholder="https://devices.example.com" autocomplete="off" spellcheck="false"><button data-mode="self-hosted">复制自建接入说明</button></div><p class="hint">连接已经部署的服务;不会替你部署服务器或迁移原有绑定。</p></details>
27
+ <p id="copy-result" role="status" aria-live="polite"></p>
28
+ <details id="instruction-details"><summary>查看或手动复制完整说明</summary><textarea id="install-instructions" aria-label="Agent 安装说明" rows="9" readonly spellcheck="false"></textarea></details>
29
+ </section>
30
+ <section aria-labelledby="pair-title">
31
+ <h2 id="pair-title"><span class="step">2</span> 使用托管时,登录并认领自己的电脑</h2>
32
+ <ol><li>按 Agent 提示打开<a href="/login">官方设备面板</a>,使用自己的 App Services/个人站账号登录。没有账号时在账号页注册。</li><li>在面板输入本机终端显示的五分钟配对码,核对<strong>完整公钥指纹</strong>后批准。密码、私钥和配对码不要发给别人或贴到 Agent 对话。</li><li>在另一台自己的电脑重复安装并绑定同一账号。没有真实回报的设备会显示未知或未回报。</li></ol>
33
+ <p class="hint">本机模式跳过此步。绑定只允许已说明的资源回报;读取日志需要目标设备另外授权。</p>
34
+ </section>
35
+ <section aria-labelledby="try-title">
36
+ <h2 id="try-title"><span class="step">3</span> 试用时看这四件事</h2>
37
+ <div class="checks"><div><h3>资源真实吗?</h3><p>分别查看内存、磁盘和最近采集时间。</p></div><div><h3>设备容易辨认吗?</h3><p>在“设备说明”填写名称、用途、标签。服务清单由你登记。</p></div><div><h3>更新状态顺畅吗?</h3><p>点“采集一次”;休眠或停止的 Driver 不会被伪装在线。旧 Driver 需要更新。</p></div><div><h3>Agent 能读清单吗?</h3><p>让本机 Agent 调用 <code>device_center_list_resources</code>,核对设备名称与用途。</p></div></div>
38
+ <details><summary>遇到问题先查什么?</summary><p>确认 Node.js 为 24+、Driver 正在运行、电脑已唤醒、网络及系统时间正常。端口被占用时改用空闲本机端口,不停止其他程序。云端“未回报”表示近期没收到数据,不是私钥过期。</p><p>反馈时说明系统、版本、所选模式、失败步骤和去掉敏感信息的错误。不要附私钥、配对码、cookie 或日志正文。</p></details>
39
+ </section>
40
+ <aside><strong>这版的范围</strong><p>支持真实资源、设备说明、手动采集和经单独授权的日志尾读。暂不提供任意远程命令、通用文件传输、Windows 原生 Driver、自动网络扫描、离线局域网授权或本机/云端说明同步。</p><p>官方中转为试用额度:每账号每日 100 MiB(UTC),每方向 16 KiB/s;不是无限带宽服务。服务与应用为手动登记,未经自动检测。</p></aside>
41
+ </main>
42
+ <footer><a href="/install/agent.md">Agent 完整安装指南</a><a href="/install/trial.md">文字版试用指南</a><a href="https://www.npmjs.com/package/device-center/v/0.2.0-beta.2" target="_blank" rel="noopener noreferrer">MIT npm 源码包 ↗</a><span>本机安装 · 私钥留在设备 · 操作单独授权</span></footer>
43
+ <script type="module" src="/src/start.js"></script>
44
+ </body>
45
+ </html>