Files
LocalNetMsg/package.json
T
xiaoshuai 2a641b1eff feat(serial): USB 页面下线, 改成串口映射 + 串口助手
## USB 整体下线

把 USB 功能真删掉, 不是只藏 UI:

- 删 `src/main/remote/usb.ts` + `src/renderer/src/components/remote/UsbPanel.vue`
- 删 `usb` (node-usb) 和 `node-hid` 两个依赖 —— 查过, 本来就没被真正用起来
- `asarUnpack` 去掉 node-usb/usb/node-hid 的规则
- ChatView 的 tab 由 "USB" 改成 "串口"

## 串口映射 + 串口助手 (新)

`src/main/remote/serial.ts` (取代 usb.ts) 只做串口, 一个文件两件事:

- **串口映射**: 把一端的真实串口映射成另一端可用的虚拟串口
  (Linux/macOS 用 socat 建 PTY 配对, Windows 用 com0com 建 COM 对)。
  方向做成 "我用对方的" / "对方用我的" 两种, 两边都能发起。
- **串口助手**: 不建虚拟口时, 应用自己当助手 —— 字节流直接显示,
  能发文本/HEX, 能追加 LF, 能循环发送, 能清屏/存日志。

`src/renderer/src/components/remote/SerialPanel.vue` (新) 把两者做在一个面板里:
设备表 (串口 / VID:PID / 产品 / 制造商)、串口参数 (波特率/数据位/停止位/校验)、
TX/RX 时间戳 + hex + ascii 视图、虚拟口路径横幅 (可一键复制)。

## 串口助手要能调 Linux

Linux 上调串口最常见的两个坑, 都做成产品功能:

- **没权限**: 诊断 dialout 组 + socat + 串口列表, 一键提权修复
  (`sudo usermod -aG dialout $USER`, 提示要重新登录才生效)。
  "设备没插" 和 "没权限" 分开报错, 不再混成一句 "打开失败"。
- **缺 socat**: 建虚拟口前先探 socat, 没有就弹安装指引 (apt/dnf),
  免得 attach 做到一半才失败。

平台化文案也分开了: Linux 提示 dialout, Windows 提示设备管理器, macOS 提示隐私授权。

## 线上格式一个字节没动 (兼容性关键)

局域网里常有一台机器跑旧版本, 所以:

- `PROTOCOL_VERSION` 仍然 = 1
- WS 帧类型仍然叫 `'usb'`
- 只把内部类型改名 (`SerialDeviceInfo` / `SerialAttachConfig` / ...)

代价: 混版本时已下线的 USB 字节桥不可用。换来: 新旧版本聊天照常,
串口映射也照常, 不会因为升协议被踢下线。**别手贱顺手改对** (AGENTS.md 坑 #10)。

## 其它

- 主进程 `output` 事件带方向 (rx/tx), 助手才能分清"我发的"和"设备回的";
  客户端自己发出的字节不回显成 rx (无回环, S5 用例守住)
- 虚拟串口 (vcom) 模式下 app 不读内部端, 避免和 socat 抢数据
- `src/main/permissions.ts` 重写成串口专用, 删掉 USB/HID 相关诊断与修复
- 保留 `src/main/remote/vcom.ts` (socat/com0com 配对) 不动, 与 serial.ts 分离

## 验证

- npm run typecheck        ✅
- npm run selftest         ✅ 79 通过 / 0 失败 (USB 用例整体换成 S1-S9 串口用例)
- npm run build            ✅
- npm run package:linux    ✅ 新 deb (84MB)
- npm run selftest:package ✅ 4/4 (改成校验 serialport 原生绑定已解包)

真机 (CDP 驱动真实窗口) 验证过: 串口 tab 能打开, 设备表列到 32 个 /dev/ttyS*,
助手区 + hex 区在, 波特率/数据位/停止位/校验/循环发送/清屏/保存日志 按钮齐全,
方向切换会换设备列表 (我用对方的 = 对方设备, 对方用我的 = 本机设备)。
2026-10-21 18:04:55 +08:00

134 lines
5.5 KiB
JSON

{
"name": "local-net-msg",
"version": "0.1.0",
"private": true,
"description": "局域网即时通讯 (Electron + Vue 3) - 飞书风格",
"homepage": "https://github.com/localnetmsg/local-net-msg",
"author": "LocalNetMsg <dev@localnetmsg.local>",
"main": "out/main/index.js",
"allowScripts": {
"better-sqlite3": true,
"electron": true,
"esbuild": true,
"@serialport/bindings-cpp": true,
"vue-demi": true
},
"scripts": {
"dev": "electron-vite dev",
"build": "electron-vite build",
"preview": "electron-vite preview",
"start": "electron-vite preview",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json",
"typecheck:web": "vue-tsc --noEmit -p src/renderer/tsconfig.json",
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"selftest:build": "esbuild scripts/selftest/main.ts --bundle --platform=node --format=cjs --target=node20 --outfile=scripts/selftest/.out/selftest.cjs --alias:electron=./scripts/selftest/electron-stub.cjs --alias:@lydell/node-pty=./scripts/selftest/pty-shim.cjs --external:better-sqlite3 --external:serialport --external:sudo-prompt --external:ws",
"selftest": "npm run selftest:build && cross-env ELECTRON_RUN_AS_NODE=1 electron scripts/selftest/.out/selftest.cjs",
"selftest:sync:build": "esbuild scripts/selftest/sync-e2e.ts --bundle --platform=node --format=cjs --target=node20 --outfile=scripts/selftest/.out/sync-e2e.cjs --alias:electron=./scripts/selftest/electron-stub.cjs --external:better-sqlite3 --external:serialport --external:sudo-prompt --external:ws",
"selftest:sync": "npm run selftest:sync:build && cross-env ELECTRON_RUN_AS_NODE=1 electron scripts/selftest/.out/sync-e2e.cjs",
"selftest:package": "cross-env ELECTRON_RUN_AS_NODE=1 electron scripts/selftest/packaging-check.cjs",
"selftest:image:build": "esbuild src/main/image.ts --bundle --platform=node --format=cjs --target=node20 --outfile=scripts/selftest/.out/image-native.cjs --external:electron",
"selftest:image": "npm run selftest:image:build && electron scripts/selftest/native-image-check.cjs",
"package:win": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ electron-vite build && electron-builder --win --x64",
"package:mac": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ electron-vite build && electron-builder --mac",
"package:linux": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ electron-vite build && electron-builder --linux",
"package:dir": "cross-env ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/ electron-vite build && electron-builder --dir",
"postinstall": "node scripts/postinstall.js"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@lydell/node-pty": "^1.2.0-beta.12",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/xterm": "^6.0.0",
"better-sqlite3": "^11.5.0",
"electron-store": "^8.2.0",
"element-plus": "^2.8.4",
"highlight.js": "^11.10.0",
"markdown-it": "^14.3.0",
"pinia": "^2.2.4",
"serialport": "^13.0.0",
"sudo-prompt": "^9.2.1",
"ws": "^8.18.0"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.11",
"@types/markdown-it": "^14.1.2",
"@types/node": "^22.7.5",
"@types/ws": "^8.5.12",
"@vitejs/plugin-vue": "^5.1.4",
"cross-env": "^7.0.3",
"electron": "^32.1.2",
"electron-builder": "^24.13.3",
"electron-vite": "^2.3.0",
"typescript": "^5.6.2",
"vite": "^5.4.8",
"vue": "^3.5.10",
"vue-router": "^4.4.5",
"vue-tsc": "^2.1.6"
},
"build": {
"appId": "com.localnetmsg.app",
"productName": "LocalNetMsg",
"copyright": "Copyright © 2026",
"directories": {
"output": "dist-Electron",
"buildResources": "resources"
},
"npmRebuild": false,
"files": [
"out/**/*",
"package.json"
],
"asarUnpack": [
"**/node_modules/@lydell/node-pty/**/*",
"**/node_modules/@lydell/node-pty-*/**/*",
"**/node_modules/@serialport/**/prebuilds/**/*"
],
"extraResources": [
{
"from": "resources/icon.png",
"to": "icon.png"
}
],
"win": {
"icon": "resources/icon.png",
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}-${version}-Setup.${ext}"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "LocalNetMsg"
},
"mac": {
"icon": "resources/icon.png",
"target": "dmg",
"category": "public.app-category.social-networking"
},
"linux": {
"icon": "resources/icons",
"target": [
"deb"
],
"category": "Network",
"synopsis": "LAN instant messaging",
"description": "LocalNetMsg — a LAN instant messaging desktop app.",
"maintainer": "LocalNetMsg <dev@localnetmsg.local>",
"desktop": {
"StartupWMClass": "local-net-msg"
}
}
}
}