Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
*.jpeg binary
*.gif binary
*.cast text eol=lf
# Preserve verified upstream formatting; UPSTREAM.json guards these files.
vendor/node-airtunes2/src/** -whitespace
vendor/node-airtunes2/README.md -whitespace
5 changes: 3 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ updates:
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
open-pull-requests-limit: 0
- package-ecosystem: npm
directory: /apps/docs
schedule:
interval: weekly
open-pull-requests-limit: 5
open-pull-requests-limit: 0
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 0
Binary file modified .github/social-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 88 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,30 +80,44 @@ jobs:
run: npm ci

- name: Audit required runtime dependencies
id: audit
env:
RADIOCLI_AUDIT_EVIDENCE_PATH: output/release-evidence/audit-runtime.json
run: npm run audit:runtime

- name: Typecheck
id: typecheck
run: npm run check

- name: Lint
id: lint
run: npm run lint

- name: Test
id: tests
# Rendering and storage fixtures can saturate small hosted runners.
# Bound concurrency without changing assertions or test deadlines.
env:
VITEST_MAX_WORKERS: '1'
run: npm run test

- name: Package smoke harness tests
run: node --test scripts/packed-smoke.test-node.mjs scripts/install-smoke-mpv.test-node.mjs
id: harness
run: node --test scripts/packed-smoke.test-node.mjs scripts/install-smoke-mpv.test-node.mjs scripts/release-evidence.test-node.mjs scripts/audit-ci.test-node.mjs scripts/build-airplay-native.test-node.mjs

- name: Build
id: build
run: npm run build

- name: MCP stdio smoke
id: source-mcp
run: npm run smoke:mcp

- name: Package dry run
run: npm run pack:check

- name: Package lint
id: package
run: npm run check:package

- name: Data smoke
Expand All @@ -117,22 +131,46 @@ jobs:
- name: Install pinned native macOS or Windows playback fixture
if: runner.os != 'Linux'
env:
RADIOCLI_SMOKE_EVIDENCE_DIR: packed-evidence
RADIOCLI_SMOKE_EVIDENCE_DIR: output/release-evidence
run: node scripts/install-smoke-mpv.mjs

- name: Background CLI playback smoke
id: background-cli
run: npm run smoke:cli

- name: Native TUI crash recovery
id: native-tui
env:
RADIOCLI_REQUIRE_TUI_RECOVERY: '1'
RADIOCLI_TUI_RECOVERY_BUILT: '1'
run: npx vitest run src/agent/tui-recovery.native.test.ts

- name: Native network security and recovery
id: native-network
env:
RADIOCLI_SMOKE_EVIDENCE_DIR: output/release-evidence
run: npm run smoke:network

- name: Fresh install smoke
id: packed
env:
RADIOCLI_EXPECTED_ARCH: ${{ matrix.architecture }}
RADIOCLI_SMOKE_EXECUTION_METHOD: GitHub-hosted native OS/CPU VM (${{ matrix.os }})
RADIOCLI_SMOKE_EVIDENCE_DIR: packed-evidence
run: npm run fresh:check -- --require-mpv
RADIOCLI_SMOKE_EVIDENCE_DIR: output/release-evidence
run: npm run fresh:check -- --require-mpv --require-clean

- name: Bind release evidence to this revision and artifact
if: always()
env:
RADIOCLI_CHECK_OUTCOMES: ${{ toJSON(steps) }}
run: node scripts/release-evidence.mjs output/release-evidence

- name: Preserve packed runtime evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: packed-${{ matrix.os }}-${{ matrix.node-version }}
path: packed-evidence/*.json
path: output/release-evidence/
if-no-files-found: warn

playback-smoke:
Expand Down Expand Up @@ -162,7 +200,7 @@ jobs:
cache: npm

- name: Install system playback backend
run: sudo apt-get update && sudo apt-get install -y mpv ffmpeg
run: sudo apt-get update && sudo apt-get install -y mpv mpv-mpris ffmpeg vlc dbus-daemon dbus-bin pipewire pipewire-pulse wireplumber pulseaudio-utils avahi-daemon avahi-utils shairport-sync

- name: Install
run: npm ci
Expand All @@ -171,6 +209,32 @@ jobs:
# Packed normal/omit-optional installs for this OS/CPU/Node matrix run in verify.
run: npm run smoke:playback

- name: Native network and fallback playback
env:
RADIOCLI_REQUIRE_FALLBACKS: '1'
run: npm run smoke:network

- name: Native desktop media protocol
# Real session-bus transport on both CPUs and Node lines. This does not
# establish Hyprland/Quickshell, hardware keys, or audible output.
run: |
test -f /usr/lib/mpv-mpris/mpris.so
dbus-run-session -- npx vitest run src/platform/mpris.native.test.ts src/player/mpv-desktop-media.native.test.ts src/platform/diagnostics.native.test.ts

- name: Native alarm output restoration
# Synthetic sinks on an isolated server verify device ownership, not speakers.
env:
RADIOCLI_NATIVE_PIPEWIRE_TEST: '1'
run: dbus-run-session -- npx vitest run src/alarms/system-volume.native.test.ts

- name: Native AirPlay software receiver
# Decoded PCM, authentication, controls and cleanup; no physical receiver claim.
env:
RADIOCLI_NATIVE_AIRPLAY_TEST: '1'
run: |
sudo systemctl start avahi-daemon
npx vitest run src/player/airplay.native.test.ts

docs:
name: Docs
if: github.event_name != 'schedule'
Expand Down Expand Up @@ -206,6 +270,19 @@ jobs:
working-directory: apps/docs
run: npm run build

required-release-checks:
name: Required Release Checks
needs: [verify, playback-smoke, docs]
if: always() && github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- name: Require all maintained-platform jobs
env:
VERIFY: ${{ needs.verify.result }}
PLAYBACK: ${{ needs.playback-smoke.result }}
DOCS: ${{ needs.docs.result }}
run: test "$VERIFY" = success && test "$PLAYBACK" = success && test "$DOCS" = success

# Experimental jobs run in full guest kernels, not Linux containers. They are
# opt-in/weekly and do not establish maintained or audible-playback support.
# VM action source is pinned; upstream release assets/images are maintained
Expand Down Expand Up @@ -236,7 +313,7 @@ jobs:
sha256sum portability-artifact/*.tgz

- name: Preserve the built package and standalone harness
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: portability-package
path: portability-artifact/
Expand Down Expand Up @@ -323,10 +400,11 @@ jobs:
export PATH=/usr/pkg/bin:/usr/pkg/sbin:$PATH
# The minimal guest omits X11 libraries required by pkgsrc mpv,
# even for null audio/video. Install the matching official OS set.
# Digest: NetBSD-10.1/amd64/binary/sets/SHA512 on cdn.NetBSD.org.
# The matching 10.1 set moved to the official release archive.
# Digest: NetBSD-10.1/amd64/binary/sets/SHA512 on archive.netbsd.org.
radiocli_xbase=$(mktemp /tmp/radiocli-xbase.XXXXXX)
trap 'rm -f "$radiocli_xbase"' EXIT
ftp -q 60 -o "$radiocli_xbase" https://cdn.NetBSD.org/pub/NetBSD/NetBSD-10.1/amd64/binary/sets/xbase.tar.xz
ftp -q 60 -o "$radiocli_xbase" https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-10.1/amd64/binary/sets/xbase.tar.xz
test "$(cksum -a SHA512 -q "$radiocli_xbase")" = b6ac2195f91d7ae395ec2734c9ae2a4727014629fbdc94562531225e0d51e3b2705047e8440fcf75e1dacdcecb3cb980df7a82f838c12ad5274ba8ce95e49b72
tar -xpf "$radiocli_xbase" -C /
/usr/sbin/pkg_add pkgin
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ coverage/
*.log
*.tsbuildinfo
.radiocli-test/
.tmp/
README.proposed.md
README.preview.html
README*.preview.jpg
vendor/node-airtunes2/build/
71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,76 @@

All notable changes to RadioCLI are documented in this file.

## [0.2.5] - 2026-10-18

### Added

- Added direct shell playback, volume controls, station-name lookup, filtered
search, favorites, recents, named presets, and listening stats.
- Added a compact now-playing view with live track metadata and `--watch`,
readable terminal tables, JSON/plain output, and shell completion for Bash,
Zsh, Fish, and PowerShell.
- Added Linux desktop media controls for RadioCLI's station queue, metadata,
playback, and volume, with `radiocli media-controls auto|on|off` overrides.
- Added experimental Omarchy Quattro integration with preferred-terminal
launching and desktop/session diagnostics.
- Added experimental, opt-in Linux AirPlay with Avahi discovery, receiver
selection, passcode handling, and guided dependency setup.

### Changed

- Simplified shell help around eight everyday commands, with `help --all` for
the full reference, `play` to resume, and favorites, recent, and popular queues.
- Expanded Doctor with separate terminal, desktop media, audio, Omarchy, and
AirPlay readiness checks.
- Included station presets in library backup, restore, and concurrent saves.
- Reduced Gallop's drawing scale by 10% while preserving its proportions,
centering, and animation timing.
- Removed the twelve preview receiver scenes, retaining the original 63 styles
and Ultracode default. Retired selections migrate without resetting saved data.

### Fixed

- Kept TUI playback running after a terminal crash, recovering the active
station, queue, controls, and sleep timer when the terminal reopens.
- Recovered interrupted or stalled live mpv streams without restarting paused
sessions, finite recordings, or playback canceled by Stop.
- Prevented duplicate mpv desktop media players and kept desktop controls in
sync with the shared player, including live-edge resume and alarm transitions.
- Restored alarm volume to the original Linux output after default-device
changes and overlapping alarms, and kept imported alarm IDs distinct across
case-insensitive and Unicode-normalizing filesystems.
- Preserved AirPlay mute, volume, and station metadata during connection setup,
added legacy RAOP password authentication, and improved connection errors.
- Protected concurrent library saves and preserved the latest data in the safety
backup before a restore. Retried temporary Windows file-replacement conflicts.
- Preserved migrated custom-station IDs, corrected XSPF CDATA and Unicode URL
imports, and restored Windows drive-path playlist imports.
- Corrected mixed Arabic/Latin terminal layout and emoji clipping, padding, and
scrolling without changing plain output or screen-reader behavior.
- Expired future-dated provider cache entries after clock corrections and
preserved valid data when migrating older caches.
- Preserved package-manager paths during Unix updates and allowed slower
PowerShell startup when opening native Windows terminals.
- Fixed a documentation typecheck race and added a single required CI gate for
platform, playback, and documentation checks. Release checks now include
packed upgrades from 0.2.3 and 0.2.4 and native TUI crash recovery.

### Security

- Removed the optional AirPlay sender's vulnerable elliptic dependency, using
Node's native Ed25519 while preserving pairing formats and optional installs.
- Enabled native stream certificate verification and restricted FFmpeg network
playlists that cannot safely carry TLS policy. Documented the remaining
native OpenSSL numeric-IP certificate identity limitation.
- Kept Doctor's probes read-only, bounded local service checks, and excluded
receiver codes and stream credentials from AirPlay diagnostics.
- Updated documentation and development dependencies to patched releases.

Quattro integration and Linux AirPlay remain experimental. Software playback
checks do not establish physical receiver, speaker, hardware-key, or suspend/wake
support; existing platform support tiers are unchanged.

## [0.2.4] - 2026-09-17

### Added
Expand Down Expand Up @@ -475,3 +545,4 @@ Initial public release.
[0.1.3]: https://github.com/Ciphore/RadioCLI/releases/tag/v0.1.3
[0.1.2]: https://github.com/Ciphore/RadioCLI/releases/tag/v0.1.2
[0.1.1]: https://github.com/Ciphore/RadioCLI/releases/tag/v0.1.1
[0.2.5]: https://github.com/Ciphore/RadioCLI/releases/tag/v0.2.5
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ a local WAV file; mpv uses null audio in deterministic package
checks. These checks exercise the player process and controls, and do not prove
speaker output. `npm run smoke:data` intentionally contacts public providers.

Also run `npm run smoke:network` for playback or transport changes. It uses
loopback HTTP/TLS fixtures with null/dummy audio to verify certificate rejection,
live EOF and stall recovery, finite-media EOF, Stop ownership, and installed
fallback readiness. Set `RADIOCLI_REQUIRE_FALLBACKS=1` when the test host should
have FFmpeg, ffplay and VLC; missing tools then fail instead of being skipped.

Run `npm run build && npm run smoke:cli` when changing shell playback or session
control. It uses an isolated library and silent local WAV with mpv's null audio
output to check background startup, separate CLI controls, presets, and shutdown.

Live provider checks are useful before releases, but ordinary pull requests
should stay deterministic. Run `npm run verify:release` for a full maintainer
pre-release pass that includes live data, playback, packaging, and fresh-install
Expand Down
Loading
Loading