aboutsummaryrefslogtreecommitdiffstats
path: root/packages
Commit message (Collapse)AuthorAgeFilesLines
* chore: bump version to 0.19.0Christophe Besson10 hours8-8/+8
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): stop the node at Quit in "only while open", whoever started itChristophe Besson10 hours3-12/+81
| | | | | | | | | | | | | | | | | | | Switching from "at sign-in" to "only while MeshBay is open" left the node the sign-in launcher had started running after Quit: only a node this process had started was stopped. In that mode the app owns the node, so Quit stops the one that is there. The start with the app and the sign-in's own start (ensureNode) also both ran `autostart start` at launch -- three meshbay-node.exe were seen racing for the port. The sign-in's start and node:start now wait for the launch's. The end-to-end test covers the mode: Quit leaves no node, opening the app starts one. It launches the app with the environment it was imported with: the suite's conftest points HOME, USERPROFILE, LOCALAPPDATA and APPDATA at a throwaway directory per test, and the app started under that crashed at once (0x80000003), which first looked like a crash of the app itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(node): check the startup mode in the Windows end-to-end testChristophe Besson10 hours1-5/+22
| | | | | | | | The boot task or the sign-in launcher, as chosen, and the node in session 0 for the service's S4U logon or in the signed-in session otherwise. Passed in service and sign-in modes on the installed 0.18.0 build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: set the Windows node up at sign-in, and stop it for realChristophe Besson10 hours23-34/+850
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Found by the first Windows beta tester, then reproduced on a clean install. After a service-mode install nothing set the node up for the account that signed in: the boot task started a node that quit ("hub.username not set"), and the sidebar showed Node / Create group only once the hub held a node key. The only way to the wizard that provisions was the home page's welcome card, which an account already in a group never sees. The way out was `meshbay-node init` and the key pasted on the profile page -- which is also what PACKAGING-GUIDE.md told people to do. - main.js `node:ensure`, called by app.js at sign-in: provisions, starts and links the node this build ships (Windows, bundled node only). A node set up for another account, or an account linked to another node, is left alone. node:start waits for it, so the two never race. - The sidebar shows the Node section when a node exists on this machine. - The Node page's status is the node's: its control API and the process list, not the service task's state (a node started from a terminal ran while the page said Stopped). Stop says Stopped only once no meshbay-node.exe is left, and stays offered for a process that answers nothing. - CLI stop kills the pid that answered when a graceful stop does not finish, and fails with the reason when a node process is still there. - The daemon ends its process 3s after _shutdown(): Python's exit waited for a busy indexer thread, with the control API already closed. Armed by main() only, never by a daemon run inside a test. - node.toml is read as utf-8-sig (PowerShell 5.1 writes a BOM), and a config that cannot be read is logged instead of dying silently in service mode. - "Pair this browser" queues the code for the next group of this node to open instead of saying "Paired successfully"; no banner before a group. - test_e2e_windows_app.py (opt-in, MESHBAY_WIN_E2E=1) drives the installed app against a throwaway hub: fresh account to linked node, Stop, Start, Restart, checked against the real processes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): drop a directory moved out of the root on Windows tooChristophe Besson10 hours2-0/+19
| | | | | | | | | On Windows watchdog cannot tell what a deleted path was -- it is gone -- and reports a directory moved out of the root as a file deleted, so the fix in 3d5a168 never ran there and test_the_watcher_reports_a_directory_moved_out failed. A deleted path the index still holds entries below was a directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: read UTF-8 explicitly in the licensing and keyring-vector testsChristophe Besson10 hours2-26/+42
| | | | | | | | | | On Windows a bare `read_text()` or `subprocess.run(text=True)` decodes with the locale's code page (cp1252), not UTF-8. `test_licensing.py` then failed on a byte of the vendored LICENSES.txt, and `test_keyring_vectors.py` decoded the generator's output, which carries CJK test strings, into something that no longer matched keyring.json. Both files are UTF-8; say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): name members admitted without an invitation nameChristophe Besson10 hours9-6/+89
| | | | | | | | | | | A member who joined by link, by a new device or into an open group was pinned in the roster with no name, so the audit log showed only the first characters of their id. The hub's MNP token now carries the account's username, and after the handshake the node writes it into the roster for an account whose name is empty. An invitation's name is never overwritten; the name is a label, authority stays on `sub`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(node): list a group's directories off the event loopChristophe Besson11 hours7-120/+167
| | | | | | | | Every full index walked all roots on the loop, and a node with several large roots stopped answering for seconds. Walk directories only, on the roots' disk thread; index_sync is spawned and still answers on failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): keep "add a directory" when one node check is missedChristophe Besson11 hours2-13/+125
| | | | | | | | A node busy indexing could miss the single 3 s check after a root was added, and the button stayed hidden until a reload. Ask up to four times before deciding there is no node on this machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): remove firewall rules and boot task on uninstall, unaskedChristophe Besson19 hours3-30/+44
| | | | | | | The Yes/No before it defaulted to No, so they stayed behind. Read both unelevated and raise the UAC prompt only when one is left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): drop a directory moved out of the root from the indexChristophe Besson20 hours2-8/+151
| | | | | | | | Watchdog reports such a move as one "directory deleted" event and nothing for the files, which the indexer ignored until the next reconcile. The freeze rules still apply: root live, directory gone, parent present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): let a download wait out a reconnect instead of failingChristophe Besson21 hours3-7/+127
| | | | | | | | Chunks sent while the reconnect's connect() runs throw at once, and six retries 1.5 s apart ran out before the reconnect landed. Wait for it, up to two minutes, without spending retries. Follow-ups parked in §15.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): move "clear finished" into the finished group's headChristophe Besson21 hours3-16/+46
| | | | | | | | 9269374 let the transfers header wrap, which broke the one-line header test_layout_measured enforces. The button now sits beside what it clears, and the header is title and summary only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): stop the transfers panel scrolling sidewaysChristophe Besson23 hours1-1/+7
| | | | | | | The header (title, summary, clear button) overflowed the 330 px panel in French. Let it wrap, with a gap, and hide horizontal overflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): prefer the bundled ffmpeg over PATH in the frozen buildChristophe Besson23 hours2-2/+59
| | | | | | | | shutil.which never looks beside meshbay-node.exe, and node-runtime is appended to the user PATH, so any earlier ffmpeg.exe on PATH ran instead of the pinned copy. Log the resolved media tools at startup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): play video on iPhone through ManagedMediaSourceChristophe Besson33 hours11-2/+24
| | | | | | | An iPhone has no MediaSource; every film was refused as an unsupported codec. A browser with neither now says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(hub): say "N groups unreachable" on Search for a few seconds, not for good0.18Christophe Besson3 days7-34/+86
| | | | | | | | | | | | The line sat above the results for as long as the page was open. It is now said once a cross-group pass is over, for five seconds, in the same passing note as "Link copied" — moved out of copy-link.js into note.js (`say(text, ms)`), one note at a time for the whole page. The `.search-unreachable` rule goes with the line it styled. The copy-link probe now also checks in Chrome that the note goes by itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(hub): join from an invitation link and land under the group's handleChristophe Besson3 days2-5/+35
| | | | | | | | | | | The invitation probe listed no groups, so after Join the address stayed the #/group/<id> the button navigated to and the group links' rewrite to #/name@owner was never exercised on that path. A third case has the hub list the group once joined, as it does: the group page opens, the address shows the handle without a history entry of its own, and the code still never reaches the hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: copy a file's or folder's #/name@owner link from Files, Music, Photos ↵Christophe Besson3 days21-20/+560
| | | | | | | | | | | | | | | | | | | | | | | and Search "Copy link" puts the address group-link.js resolves on the clipboard, on the hub's origin rather than the page's, so a link copied in the desktop application is not app://meshbay. Files offers it for one row, from the right-click menu or the toolbar with one row ticked (a phone's way in); Music on one track's menu, whose dots a phone has; Photos on a right-clicked tile and in the lightbox's bar. The video player and the file preview carry a link button next to Download. Applications get a `linkFor(entry | folderPath)` prop (MESHBAY_DESIGN.md §9.2) and offer the action only when it names a link. The group page builds it from the hub's row; Search from each result's own group and its path before the merged views prefixed it, and names no link for a folder of the merged tree, which a group name alone does not identify. harness/copy_link_probe.py mounts the three applications in Chrome and reads what reached the clipboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: open a group, a folder or a file from a #/name@owner linkChristophe Besson3 days18-11/+615
| | | | | | | | | | | | | | | | | | | A group can now be reached by the handle shown under its name, and a path after it points inside the group: #/name@owner/root/dir/file downloads the file and opens Files on its folder; a folder opens Files there. The handle is resolved in the client against the account's own /v1/groups/mine, so no hub route answers for a name and nobody can probe for one. While a group is open the address shows the handle (replace, no history entry); a linked path is taken out of the address once acted on, so a reload does not download twice. Signing in no longer sends everyone home: the form stood in for the page the address named, and that is where a link opened signed out was going. group-link.js holds the parsing and lookups, executed whole by test_group_link.py; harness/group_link_probe.py drives the router in Chrome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: list as members only the accounts the node has admittedChristophe Besson3 days13-6/+55
| | | | | | | | | | | | | | | | The Members list showed the hub's membership, which an account gains when it accepts the invitation or redeems a link, before it has presented its code to the node. The node's roster is the authority (MESHBAY_DESIGN.md §3.4), so the list now crosses the hub's members with the sealed group roster the node already sends every connected member. An account the node has not admitted yet is shown to the owner alone, as waiting for its code, with the Remove button; other members do not see it. When the roster cannot be read, the hub's list is shown as before. groupRoster() takes { fresh: true } so the page sees who joined since the connection opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): plug an auto-ejected removable root back once its files returnChristophe Besson3 days7-7/+234
| | | | | | | | | | | | | | | | | A node started with the desktop session runs before the session has mounted its USB drives. The safety net then auto-ejected every removable root and persisted it exactly like an operator's eject, so after each reboot those roots stayed ejected until someone plugged them by hand (seen on a node whose /media drives were mounted a minute after it started). An auto-eject is now stored as such ("auto" in roster.db). At startup and at every reconcile, an auto-ejected root whose path is readable again is checked against a few files the hash cache knows under it, at the same path with the same size and mtime; one found and the root is plugged back and rescanned. An empty mount point or another drive in its place is not recognised and stays ejected. An operator's eject is never undone automatically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: move the desktop client's Hub section from Settings to ProfileChristophe Besson3 days12-35/+35
| | | | | | | | | | | The hub address is where the person's account lives, so it sits with the account: on the Profile page, after Sessions and before deleting the account. Settings keeps "Keys on this device", which describes the machine. The hint now says what the setting is: the hub this application connects to, and that changing it signs you out while the account stays on that hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: generate an HTTP API listing for the hub and the node control APIChristophe Besson3 days23-4/+138
| | | | | | | | | | | | | | | | | | | docs/MESHBAY_HTTP_API.md lists every route of the hub (by domain, with the authentication each requires) and of the node's loopback control API. It is written by docs/generate_http_api.py from the routes and their docstrings; test_http_api_doc.py fails when the file drifts from the code or when a route has no docstring, so a new route must say what it does. 79 routes had no docstring and get a one-line description; a few whose first line did not describe the route get a summary line. The login page's developer docs gain an API link next to Design and Protocol, in every language. README, MESHBAY_DESIGN.md (§0.1, §6.7, §7) and CLAUDE.md point to the listing; README also points to examples/. The examples scripts with a shebang become executable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat: prefill https://meshbay.org on the desktop client's hub screenChristophe Besson3 days12-15/+4
| | | | | | | | The first-run "Which hub?" field now starts filled with meshbay.org; it is still asked, so the client can be pointed at another hub. The note under the form, which explained there was no default, is removed with its catalogue key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(node): print "running" once in meshbay-node statusChristophe Besson3 days1-1/+2
| | | | | | | | The daemon line repeated the state the daemon reports ("running — running"). The state is now appended only when it says something more than "running", such as waiting_for_hub. The QUICKSTART example is updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: read the node token per call and detect before polling the index dockChristophe Besson3 days2-11/+24
| | | | | | | | | | | | | node:op only knew the daemon's token once a page had called detect(), and kept it after the daemon replaced it on restart: the index dock stayed empty on a node machine until the Node page was opened, every operation answered 401 after a node restart, and on a machine without a node each 30 s poll was a rejected IPC call Electron printed to the terminal ("Node not detected"). node:op now reads the config and token from disk on every call, and the dock asks detect() before its first operation and after any failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: license MeshBay — LGPL protocol layer, AGPL for the restChristophe Besson3 days37-3/+4354
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | The protocol layer is LGPL-3.0-or-later in every language it exists in, so any client may use it whatever its own licence: meshbay-common, and the files marked with an SPDX line — keyderive.js, crypto.js, playlist-crypto.js, transport*.js; keyring.js, transcripts.js and argon2-wasm.js on the desktop; Kdf.kt, Keyring.kt and Transcripts.kt on Android. Everything else is AGPL-3.0-or-later, which the RPM specs and package.json already declared without a licence file to back them. Two AGPL section 7 permissions: - group applications may be under any licence when they use the interface only through a named surface (static/licenses/APPLICATION-EXCEPTION.txt); the reference application is 0BSD so that copying it brings no AGPL code; - the Android application may be conveyed linked with Google Play services. Third-party code is accounted for: THIRD-PARTY-NOTICES.txt is generated from what a build ships (packaging/third_party_notices.py) for the deb/rpm venv and the frozen Windows node — PyAV's wheel grafts in libx264 and libx265, which its BSD licence does not mention — and the vendored browser libraries get their licence texts and htm-preact.js its provenance. Wheels carry SPDX metadata, RPMs %license, debs a DEP-5 copyright file, every Windows target LICENSE.txt. test_licensing.py holds the line: the LGPL layer imports nothing under the AGPL, the reference application nothing outside the application interface, and every SPDX line is one of the known ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): launch icon stays up for at least half a secondChristophe Besson3 days1-0/+23
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(cast): music on the TV, the music bar as its remoteChristophe Besson3 days21-408/+1274
| | | | | | | | | | | | | | | | | A cast button in Music's toolbar and in the music bar. With a television chosen, each decrypted track goes to the relay with its cover — found as the album card finds it — and plays there as music with its title, artist and album; the bar's play, pause, seek, previous and next drive the receiver, its clock is the receiver's, and the end of a track there moves the queue on. A film or a photo taking the television pauses the bar; stopping the cast carries the track on locally. Photos and tracks now share one path: a whole file sent to the relay in pieces (binary frames on Android, written to disk there), served at /file with byte ranges and its cover at /cover, and loaded as what the relay says it is. cast:image is gone; cast:chromecast:seek is new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(cast): photos on the TV, and a television chosen once for the sessionChristophe Besson3 days28-45/+1137
| | | | | | | | | | | | | | A cast button in Videos' toolbar, at the top of Photos, in an album's bar and in the lightbox, in a group and in Search alike. A television chosen there is kept for the session: a film opened plays on it with the player as its remote from the start, and a photo opened in the lightbox is shown on it, scaled to 1920x1080, upright, as JPEG. The lightbox gains a slideshow. The relay serves one photo at /image behind the stream's token, on the desktop and on Android; the shell, not the page, decides that the receiver loads it as a picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): music keep-alive outlasts a skipped trackChristophe Besson3 days1-7/+15
| | | | | | | | Held while a track plays or loads and released 5 s late, so skipping a bad file with the screen off no longer drops the Android foreground service, which cannot be taken back from the background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): music keeps playing with the screen offChristophe Besson3 days11-20/+81
| | | | | | | | While a track plays, the page asks the shell to stay awake (playback:keep-alive): on Android the cast's foreground service and visible WebView, with a notification; on desktop a power save blocker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(hub): music player retries a track lost to a screen-off disconnectChristophe Besson3 days1-3/+60
| | | | | | | | A transport failure while the page is hidden, or within 30 s of waking, keeps the track and its spinner and retries once the page or the connection is back, instead of skipping it with an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): music player starts on its own and no longer shows autoplay ↵Christophe Besson3 days2-2/+7
| | | | | | | | | refusals WebView now allows play() after the track fetch; a NotAllowedError leaves the track waiting for the play button instead of raising an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: bump version to 0.18.0Christophe Besson5 days8-8/+8
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Player: redesigned cast remoteChristophe Besson5 days14-74/+254
| | | | | | | Skip buttons with circular arrows, filled scrubber, large play/pause, device header; the remote is its own component. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Player: remote control while castingChristophe Besson5 days14-9/+280
| | | | | | | Shows the receiver's position with play/pause, ±30 s and a scrubber; a seek restarts the relay where asked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cast: receiver position and pause/play on both clientsChristophe Besson5 days7-0/+102
| | | | | | | The player's remote mode reads where the television is instead of the local playhead, which drifts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): cast context created at launch, session found if its callback ↵Christophe Besson5 days2-2/+28
| | | | | | | | | is missed As the SDK recommends; and a connected session the listener did not hear of still counts, so a missed callback no longer fails the cast. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): the cast header is everything before the first moofChristophe Besson5 days3-3/+74
| | | | | | | | The node's first chunk can be the 28-byte ftyp alone, the moov in the next; served as the header, the receiver had no moov and gave up. A receiver early for the header now waits for all of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(android): release builds signed with the debug key for nowChristophe Besson5 days1-1/+7
| | | | | | | Not debuggable, no WebView devtools, no console forwarding; installs over a debug build and back. A stand-in until the release key (Stage D12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): the cast relay spools a receiver's lead to diskChristophe Besson5 days4-44/+143
| | | | | | | | Fragments are 5-10 MB at a film's bitrate; dropped past 8 MB in memory, the TV froze for their length. Each receiver now reads from its own spool file, deleted with it; nothing is dropped short of a disk bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): log what a cast does downstream of the relayChristophe Besson5 days2-3/+57
| | | | | | | | Fragments dropped for a slow receiver, writes that block, a periodic per-client summary, and every receiver state change with its position — the only trace a freeze on the television leaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): the MeshBay iconChristophe Besson5 days10-0/+46
| | | | | | | The desktop client's icon in the adaptive icon's safe zone, over its own edge colour, so no launcher mask crops the M. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): the local player is silent while castingChristophe Besson5 days2-0/+33
| | | | | | | It keeps playing to pace the relay, so it doubled the television's sound. The viewer's mute setting comes back when the cast ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(android): cast relay serves a clean header and restarts on its portsChristophe Besson5 days5-12/+159
| | | | | | | | The init is what precedes the first moof; ports are reused like Node's; the SDK is read on the main thread; a cast that fails says why on screen, and success waits until the receiver actually plays. Never a VPN's address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(client): a seek's first segments are held while it lands, not droppedChristophe Besson5 days3-3/+106
| | | | | | | | They are the new stream, header first. Dropped, a cast relay restarted at the landing got no ftyp/moov and the receiver gave up; they are now replayed in order once reinitAt/resumeAt is done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): casting through a LAN relay and the platform cast SDKChristophe Besson5 days17-35/+1176
| | | | | | | | A port of cast-relay.js (backlog also bounded in bytes), discovery and control with the default media receiver, relay calls kept in order, and a foreground service plus a WebView kept visible so a cast survives the screen going off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(android): downloads to disk and uploads through the system pickerChristophe Besson5 days10-11/+624
| | | | | | | | Native save over the Storage Access Framework and MediaStore, chunks sent as binary bridge messages, a chosen folder that has gone asks rather than redirects, unfinished files removed on abort and after a killed process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>