Commit Graph

695 Commits

Author SHA1 Message Date
watermeko
5572e1de35 feat(storage): browse a medium and exchange files with the target machine (#957)
* feat(storage): browse a medium and exchange files with the target machine

The image menu and the download page are replaced by one storage page: it
lists the media the emulated USB disk can carry, mounts and ejects them, and
browses the files inside a medium over that same gadget, so the two machines
exchange files without going through the terminal.

Mediated media:

* Add the media API (list, state, mount, eject, delete, download, files) and a
  file browser on top of it, and keep the pre-2.6 image endpoints working as a
  compatibility layer over the same code.
* Add diskfs: an image is mounted through a loop device so the kernel's exFAT
  and FAT drivers read it, and the data partition is browsed in place, because
  the emulated disk already hands that very directory to the target machine.
* Keep the one-writer policy the read-only mounts introduced: an image is
  served read-only in every mode, and the whole data partition is remounted
  read-only here while the target machine is its writer, through the same
  SetDataWritable the virtual disk switch uses. The file manager refuses those
  writes itself as well, so a request does not depend on a mount state.
* Name the mounted medium and the USB device after the image file, cut to the
  SCSI inquiry product field and the USB product string, and restore the
  defaults when the medium is released.
* Close the HID devices around a gadget re-enumeration and reopen them after,
  since mounting and a forced eject drop them.
* Report transfers through one job slot with progress, queueing behind a
  running transfer, and a free space preflight before an upload starts. Image
  uploads keep the volume descriptor and partition table check, so an unusable
  image is rejected while it streams.

The shared folder view:

* Refresh it on a timer while the target machine owns the partition. The
  target's block level writes land in this device's page cache, which the
  mounted exFAT shares, so the file data here is already current - but the
  dentry and inode caches are not, and a file the target grew keeps the size
  it was first read with. Dropping those caches and pushing a storage event
  makes the web UI follow the target machine within a second of its own
  writeback. The partition's write counter cannot drive this: it only moves
  once this side writes its dirty pages back, which is tens of seconds later
  or never.
* Send the refresh notifications from their own goroutine, so a client that
  stopped reading cannot hold up the refresh loop.

The web UI:

* Add the storage page: the medium list with mount and eject, the file browser
  with search and sorting, uploads, downloads and deletes, and transfer
  progress.
* Gate writing on the target machine owning the disk - image uploads and file
  uploads alike - instead of letting the server answer with an error the page
  has to surface, and drop the medium rows' download and delete buttons there
  too.
* Give the shared folder, .iso and .img their own glyphs, and pick the file
  icon from the extension, so a listing can be read at a glance.
* Name the partition the way the storage page does in the desktop menu as
  well, instead of showing the API's untranslated string.
* Read the mounted medium in Settings - Device from the media state, now that
  the storage API it used is gone.
* Tell an ISO 9660 image apart from a filesystem that cannot be read at all:
  the kernel of this device carries no iso9660 support (CONFIG_ISO9660_FS), so
  the contents of an ISO cannot be listed while the image itself is served to
  the target machine like any other medium.

Localization:

* Translate the new strings for every locale, keep the Settings - Device
  strings the read-only mounts added, and drop the keys the retired image menu
  and download page left behind.

Chores:

* Remove the desktop image menu, the download page and the API they used.

* fix(storage): harden media file transfers

Serialize medium deletion with gadget attachment and restore /data write access after failed mount transitions.

Use available filesystem space for upload limits, refine virtual-device and shared-file handling, and update media path and installation test coverage.

Pin an image while it is streamed out, so a writable mount cannot start between the check and the last byte; sweep stale upload temporaries from the whole data partition at startup, which the file manager itself cannot delete; and preflight URL downloads against the response size, which only the upload path did.

Show dot files in the file browser again: only the two transfer temporary prefixes stay hidden, which is what the dot filter was for.

Delete folders recursively after the web UI confirms, refusing when an attached or mounted medium lives inside the directory; accept every .iso and .img upload by name instead of sniffing volume descriptors; and show image and file transfers in one progress display at the top of the storage page.

* fix(storage): make cache drops work and close delete and gadget races

- drop_caches only accepts 1..4 on 5.10: write 4 once to silence the log,
  then 2/3. Writing 6/7 failed with EINVAL, so the shared folder never
  saw what the target machine wrote.
- Resolve a delete path to its on-disk spelling (exFAT is case-insensitive)
  before checking it against the attached and loop-mounted media, refuse
  deleting a loop-mounted image or a folder holding an upload target, and
  fail closed when the disk state cannot be read.
- GetMediaState no longer fails when the virtual disk is off and lun.0 does
  not exist; browsing works with the virtual disk off.
- An explicit enable for the virtual disk compares against the disk being
  configured, so it leaves a served image alone; the settings toggle keeps
  the #937 semantics.
- Restore /data write access after a failed gadget script or network switch.
- eject clears the LUN file before resetting ro/cdrom, so a refused eject
  does not change the type of the medium still being served.
- Mount loop images with MS_NOSYMFOLLOW and open files with O_NOFOLLOW.
- Accept UDF images as optical media.
- Only refuse deleting transfer temporaries while a transfer runs; sweep
  stale ones in the background, by exact name, and not on a read-only /data;
  hide them from listings.
- Cap URL downloads without Content-Length at the free space and verify the
  received length.
- Encode Content-Disposition filename* strictly per RFC 5987.
- Restore image_test.go and add tests for the above.

* fix(web): keep the file browser and transfers consistent on the storage page

- Tag a listing with the medium and path it belongs to, so entries of the
  previous directory are never shown or acted on under a new path.
- Poll the desktop storage menu only while it is open.
- Show a dedicated message for a checksum mismatch.
- Bring back the USB / SCP / TF card hints behind a help button.
- Pick up a transfer already running when the page opens; make uploads
  cancellable; track concurrent uploads separately; let a failed job be
  dismissed; show a success state and guard the progress display by task.
- Handle request errors that were left unhandled.
- Dedicated confirmation for deleting a folder recursively.
- Treat UDF images as optical, send credentials on the image download
  pre-check, translate the remaining Hungarian string.

---------

Co-authored-by: Guoguo <i@qwq.trade>
2026-10-19 09:44:30 +08:00
watermeko
d0ff328db8 fix(web): import react-simple-keyboard by its named export
`react-simple-keyboard` ships only a UMD/CJS bundle whose `module.exports`
is `{ KeyboardReact, default: KeyboardReact }` with `__esModule: true`.

Vite 8 applies Node-compatible CJS interop for ESM importers, so the
default import resolved to the whole `module.exports` object instead of
the component. Rendering it made React throw "Minified React error #130"
(`got: object`) as soon as the on-screen keyboard was opened.

Import the `KeyboardReact` named export, which is the same component,
and keep `KeyboardButtonTheme` as a type-only import.

Fixes #936
2.5.2
2026-09-29 10:15:53 +08:00
watermeko
ff45b46c49 feat(storage): mount images read-only and hand /data to one writer (#937)
Only one side may write the mounted image or the /data partition at a time,
and uploaded images are validated before they are stored.

Image mounts:

* Serve every mounted image read-only, in CD-ROM and mass storage mode alike:
  detach the LUN first and set the read-only flag before the image is
  attached, so the target machine can neither write to nor format it. The
  flags were previously only written for CD-ROM mounts, so a mass storage
  mount inherited ro=0 and the image stayed writable from the host.
* Report the virtual disk as on only while the LUN really carries the /data
  partition. With an image mounted the switch used to stay active but
  unchangeable, and enabling the disk is now a confirmed action instead of a
  rejected one: it cancels the mounted image, and the confirm also warns that
  the image download is interrupted because /data becomes read-only here.
* Ask before mounting: the image list warns that the virtual disk is released
  for the image, or offers to enable it first when it is off, instead of
  dropping the /data share silently. The new strings are translated for every
  locale.
* Name the mounted medium and the USB device after the image file, cut to the
  16 bytes of the SCSI inquiry product field and the 126 bytes of the USB
  product string, and restore the previous names on unmount.
* Reset the LUN flags on every gadget restart. They survived a restart, so
  the shared partition came back to the host read-only, or as an optical
  drive, after an image had been mounted.

The /data handover:

* Mount /data read-only at boot whenever the virtual disk serves the raw
  partition as a writable disk, so the handover holds from the first boot on.
* Add SetDataWritable(), which remounts /data and verifies the new state.
  Enabling the virtual disk makes /data read-only before the host can write
  it, and refuses to expose the disk when the remount fails instead of taking
  the risk. Unmounting an image, which hands the raw partition back to the
  host, does the same, while mounting an image switches /data back to
  writable because a served image is read-only for the host.
* Cancel a running image transfer before the handover and wait for it to
  stop, and report the state in the image list, where mounting still works
  while deleting and uploading do not. The delete action is disabled with a
  hint, and the downloader offers to turn the virtual disk off.

Image uploads:

* Validate the volume descriptors while streaming, so an unusable image is
  rejected in seconds instead of after the whole transfer: accept ISO 9660 and
  UDF volume descriptors (CD001, BEA01) as well as raw disk images (GPT, APFS,
  HFS+, MBR), and reject anything else. Require the image to fit in /data with
  a 128 MiB reserve before any of it is received, log the underlying errors on
  the rejection paths, and keep the temporary file, the progress ticker and the
  close handling in defers.

Add unit tests for the upload header check, the free space preflight and the
image name truncation.
2026-09-24 09:17:24 +08:00
Guoguo
bd070b2f5c docs: add 2.5.1 changelog entries (#929) 2.5.1 2026-09-09 16:38:14 +08:00
watermeko
5c0bef019f fix(terminal): start a root login shell
Launch Web terminal sessions in /root as login shells so they load the root profile and display the configured prompt.
2026-09-04 17:25:51 +08:00
watermeko
283c1390e5 feat(shell): add informative SSH prompt
Create a default root profile when one does not already exist. Show the user, hostname, and working directory while preserving custom profiles.
2026-09-04 17:25:51 +08:00
watermeko
7f95fe9bbd fix(stream): isolate H264 subscribers
Keep slow WebRTC consumers from blocking the shared H264 source by dropping buffered frames until the next keyframe. Restore the WebRTC NACK and RTCP report interceptors while retaining a safe RTP MTU, and enable linker --as-needed for libkvm to remove unused OpenCV dependencies.
2026-09-01 15:34:20 +08:00
watermeko
a9e1ef2459 perf(stream): share H264 capture frames
Route Direct and WebRTC consumers through one H264 capture source to avoid duplicate VENC reads. Overlap WebRTC capture with ordered RTP/SRTP writes and packetize each access unit once for all WebRTC clients.

Add libopencv_video.so.409 into dl_lib.
2026-09-01 15:34:20 +08:00
watermeko
2ba45a2147 feat(web): enhance configurable input regions
Make input-region calibration work across MJPEG and H.264 video modes, including robust four-sided black-border detection and initial-selection edge snapping. Persist multiple reusable manual regions, keep manual and original-resolution choices mutually exclusive, migrate legacy settings, and localize the new controls for every supported language.
2026-08-25 09:36:13 +08:00
watermeko
9e6d280826 feat(web): add configurable mouse input regions
Add device-level input-region APIs for reading, validating, and atomically persisting absolute mouse calibration settings, including automatic, manual, and disabled modes.

Introduce a shared screen viewport that keeps the rendered media geometry and configured input region aligned across MJPEG, direct H.264, and WebRTC streams. Add automatic black-border detection and a manual selection overlay with dragging, resizing, magnified preview, keyboard locking, and cancellation support.

Provide original-resolution presets so manual calibration can be recalculated for common aspect ratios, expose the controls from the desktop mouse menu, close menus before entering selection mode, and preserve active file transfers when the download menu closes.

Add localized control-region labels and messages for all supported locales, and update direct-frame rendering so media dimensions are reported after the canvas has been drawn.
2026-08-25 09:36:13 +08:00
watermeko
527caeff6a fix(web): correct mouse coordinates for direct h264 frames 2026-08-25 09:36:13 +08:00
肆月
71ab9127dd feat: add secure multi-user support (#876) 2026-08-20 14:29:56 +08:00
肆月
d382f0621d fix: report and retry WebRTC connection failures (#866) 2026-08-13 14:56:01 +08:00
watermeko
9415afb359 fix(mouse): improve touch interaction and screen fitting (#857)
- Add touch gestures for relative mouse mode, including tap, drag, long press, double-tap drag, and two-finger scrolling.

- Improve absolute mouse touch handling and prevent black letterbox areas from generating remote mouse events.

- Fit H.264 and MJPEG screens with contain scaling and disable browser touch handling on the screen elements.

- Move golang.org/x/sys to the direct dependency list.
2026-08-11 09:47:04 +08:00
肆月
e5f6dfabaa fix(ota): isolate updates in persistent workspaces (#863)
* fix(ota): isolate updates in persistent workspaces

Stage online and offline update archives under /root/.kvmcache/nanokvm-update-* and validate storage, manifests, and archive contents before changing the installed application.

* fix(ota): harden storage safety and release gates

Preserve the last rollback backup when update storage is insufficient, and verify the actual application mount point before installation.

Move the shared transfer sentinel from /tmp to /run, enforce device package limits in release verification, and run that verification in package CI.
2026-08-10 14:49:29 +08:00
watermeko
757e0f61c5 feature: provide HDMI status in /api/vm/hdmi (#859)
* feature: provide HDMI status in  /api/vm/hdmi

* fix: return false when HDMI idle in  /api/vm/hdmi
2026-08-07 09:43:22 +08:00
肆月
761b02ed7e feat(web): upgrade build tooling to Vite 8 (#853)
Upgrade Vite to 8.2.0 and @vitejs/plugin-react to 6.0.5.

Use Vite's native tsconfig path resolver and remove vite-tsconfig-paths.

Verified with pnpm build and pnpm lint (existing warnings only).
2026-08-06 14:25:55 +08:00
Guoguo
562ea8277c feat: add dev container for full-stack development (#861)
Layer the frontend toolchain on top of the existing release builder
image so a single container covers Go, C support-layer and web
development, with the builder image remaining the single source of
truth for the cross toolchain, Go and MaixCDK.

- Mount the workspace at /home/build/NanoKVM, where the Makefile and
  support/sg2002/build expect it, and resync MaixCDK components via
  update_lib on create so C builds never use stale sources
- Install Node from official dist tarballs (integrity-checked, pinned
  to the major used by CI), with NODE_DIST_MIRROR / NPM_REGISTRY /
  BASE_IMAGE build args for restrictive or mirrored corporate networks
- Export the MaixCDK virtualenv via remoteEnv so non-interactive
  processes (tasks, extensions, exec) get it too; make
  updateRemoteUserUID explicit since the published builder image bakes
  the CI runner's uid
- Run pnpm non-interactively during post-create (no TTY) and give the
  skeleton-less home a standard ~/.profile -> ~/.bashrc chain
- Make the web toolchain convention explicit via package.json engines
  (Node >= 22, pnpm >= 11, matching CI) and document it in web/README
- Document the dev container in the README, point server/README at the
  container flow that works on any host OS, and ignore .pnpm-store/
2026-08-06 11:00:40 +08:00
Guoguo
7abec3cbad docs: add 2.5.0 changelog entries (#860) 2026-08-06 10:18:29 +08:00
Guoguo
8fc31553f7 ci: prefix GitHub Release titles with nanokvm (#856) 2026-08-05 11:52:22 +08:00
watermeko
9e50040713 fix(ota): keep update lock through service restart 2026-08-05 11:50:39 +08:00
watermeko
0f8ef20fb5 feat(ota): verify offline update checksums 2026-08-05 11:50:39 +08:00
watermeko
7cda3b2920 feat(ota): add custom update server support
+ Add persistent custom update server configuration, authenticated URL support, manifest validation, and the settings UI.

+ Keep application updates rebootless by coordinating video shutdown, migration-time service startup, and online/offline service restarts.

+ Localize the custom update server workflow across all supported languages.
2026-08-05 11:50:39 +08:00
Guoguo
40bbeab8ae ci: split package, tag, and release workflows (#855) 2.5.0 2026-08-04 16:33:56 +08:00
肆月
eb20fb41aa feat: show remote keyboard lock status (#838)
* feat: show remote keyboard lock status

* fix(keyboard-led-status): eliminate polling and improve delivery

* fix(keyboard-led-status): simplify indicator labels
2026-08-04 09:28:14 +08:00
watermeko
21e834b4c8 perf: reduce latency in 60 Hz mode (#844)
- Add decode-driven flow control and bounded GOP-aware queues to prevent stale H.264 frames from accumulating in TCP and WebSocket buffers.
- Isolate slow clients with dedicated writer goroutines, write deadlines, and safer connection lifecycle handling.
- Move the Direct H.264 socket into the worker, simplify the low-latency decode path, and improve reconnect behavior.
2026-07-31 19:08:07 +08:00
916BGAI
8773605715 Merge pull request #845 from imguoguo/ci/release-package
ci: release package
2026-07-31 19:00:18 +08:00
Guoguo
b23eb33611 ci: build identifiable NanoKVM release packages 2026-07-31 03:47:54 -07:00
Guoguo
ef432da2fe build: allow overriding the builder image and install patchelf
Groundwork for building outside an interactive terminal.

- IMAGE_NAME becomes overridable so a prebuilt image can be used instead
  of building one locally
- docker run no longer hardcodes -it on the app and support recipes:
  allocating a TTY fails where there is none. shell stays interactive
- install patchelf, which server/build.sh needs to set the RPATH on the
  server binary
2026-07-30 19:55:30 -07:00
Guoguo
96c86f6098 fix(build): correct exit codes in support/sg2002/build
The script has no error handling of its own, so these all pass silently.

- update_lib exited 1 after succeeding, so callers could not chain it
- a failed maixcdk build printed "Build Error!" and exited 0, letting a
  caller carry on with no artifacts
- add_to_kvmapp did the same when the source did not exist
- the copies in update_lib were unchecked; since update_lib is what keeps
  the SDK components in sync with the source tree, a silent failure there
  yields libraries built from stale sources
2026-07-30 19:52:34 -07:00
watermeko
a7d7574e70 fix: warn about mixed H264 stream modes (#843) 2026-07-31 10:48:52 +08:00
watermeko
b587b9f912 fix: stop HDMI capture when logged out (#841)
* fix: stop HDMI capture when logged out

* feat: stop HDMI capture when viewers are idle

* fix: keep HDMI capture active for consumers
2026-07-30 17:29:07 +08:00
watermeko
004fd59093 fix(sg2002): harden VI state sharing 2026-07-30 16:08:48 +08:00
watermeko
774d314ce9 fix(sg2002): share VI debug state between processes 2026-07-30 16:08:48 +08:00
watermeko
e4d23014ba fix(sg2002): read VI debug state without shell pipelines 2026-07-30 16:08:48 +08:00
BeaconCat
dc050d1f19 fix(usb): assign deterministic gadget MAC so the host NIC stays stable (#828)
The USB network gadget (rndis.usb0 / ncm.usb0) is created without dev_addr or
host_addr, so the kernel picks a random MAC for both the device side and the
host side on every bind. S10uuid later pins the device-side usb0 via
'ip link set', but the host_addr -- the MAC the attached PC's RNDIS/NCM adapter
uses -- stays random and changes on every re-enumeration/reboot. The host then
registers a brand-new network adapter each time, piling up stale adapters/MACs
(e.g. ~10 after a few reboots) and breaking anything that keys off the MAC.

Derive both MACs from the chip UID (same scheme S10uuid already uses for eth0
and usb0), before binding the gadget:
  dev_addr  = 48:da:35:6e:<uid>
  host_addr = 48:da:35:6d:<uid>

Verified on a NanoKVM Cube: host_addr went from random a6:3f:5d:8a:7d:52 to a
stable 48:da:35:6d:26:62, identical across two reboots.

Fixes #740

Co-authored-by: BeaconCat <BeaconCat@users.noreply.github.com>
2026-07-29 16:29:53 +08:00
SiYue-ZO
57e1f6a4d6 fix(picoclaw): cancel runtime lifecycle on mode switch
Cancel PicoClaw start/readiness waits when MCP takes control, clear held manual input state after failed release reports, and keep paste duration below the control-mode wait budget.

Also add MCP copy failure translations and avoid reconnect effect churn when the locale changes.
2026-07-29 15:16:06 +08:00
SiYue-ZO
d578c63036 i18n(picoclaw): add MCP control translations
Add locale coverage for MCP settings, AI-control ownership, PicoClaw runtime operations, and related conflict states.
2026-07-29 15:16:06 +08:00
SiYue-ZO
6b98f5415f feat(web): add MCP settings and control handoff UI
Add the MCP settings view and frontend API bindings for configuring remote-control access.

Expose AI-control ownership state in PicoClaw, lock conflicting keyboard input paths, and preserve chat state when control is released.
2026-07-29 15:16:06 +08:00
SiYue-ZO
64ce12cc0d feat(mcp): add authenticated remote control backend
Add an MCP Streamable HTTP server with token authentication, screenshot capture, keyboard, and mouse tools.

Introduce shared control-mode and input-control coordination so MCP, PicoClaw, and local HID paths serialize ownership safely.

Integrate PicoClaw gateway/runtime control handoff with PID-managed startup and focused unit coverage.
2026-07-29 15:16:06 +08:00
watermeko
183f8dc983 fix: reuse existing container users and groups 2026-07-27 10:28:46 +08:00
watermeko
7eee77a33d fix: fix container slow caused by uid/gid 2026-07-27 10:28:46 +08:00
watermeko
c1986acf65 fix(web): keep direct H264 playback low latency 2026-07-24 15:41:02 +08:00
watermeko
c999a883bd perf(sg2002): integrate zero-copy H.264 capture
Move frame ownership and zero-copy VENC handling into the tracked MMF
and Vision sources instead of relying on a preload hook. Keep the sensor
name mapping aligned with middleware enums and release deferred VI frames
safely across encoder lifecycle changes.
2026-07-24 15:41:02 +08:00
肆月
70f8503cbb fix: prevent OLED sleep duration truncation (#834)
OLED sleep settings are stored as seconds, but the kvm_system reader parsed the value into a uint8_t before assigning it to the uint16_t state field. Values above 255 seconds wrapped, so 5 min, 10 min, 30 min, and 1 hour did not behave as selected.

Parse the setting into a uint16_t-sized temporary value and terminate the read buffer before atoi so all configured durations up to 3600 seconds are preserved.

Closes #826
2026-07-22 17:46:39 +08:00
Guoguo
5b0a7d2a91 feat: add sha256sum check (#832)
* feat(download): add sha256sum check and cancellable downloads

+ server/proto/download.go
	+ add DownloadImageReq and sha256 argument

server/router/download.go
	+ add cancel download router

server/service/download/service.go
	+ sha256 decode and check
	+ remove HEAD request
	+ cancel download

* refactor(download): synchronize image and application transfers

sovel the the problem that image downloading and app downloading would conflict
at same time

* fix: optimized UI of image download and locale issue

* fix: locale
2026-07-17 18:17:04 +08:00
watermeko
4fee758ff0 fix: fix api/storage/image/mounted error when using HID-Only mode (#833) 2026-07-17 18:02:07 +08:00
watermeko
736259baa3 fix: locale 2026-07-17 07:47:25 +00:00
watermeko
83b600bbee fix: optimized UI of image download and locale issue 2026-07-16 11:16:36 +00:00
watermeko
6071a6f57c refactor(download): synchronize image and application transfers
sovel the the problem that image downloading and app downloading would conflict
at same time
2026-07-16 11:03:07 +00:00