Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
45521a0
Update Profiles tab layout in GUI plan
May 18, 2026
ddbc0a7
Document Sensors tab Create Rule affordance in GUI plan
May 18, 2026
d620073
New GUI: three-panel Profiles tab, global Actions tab, sensor quick-c…
May 18, 2026
160a176
Rich action config UI: per-action forms replace generic text fields
May 19, 2026
104e4a5
Implement sensor run policy to reduce idle CPU usage (issue #551)
May 19, 2026
dbffaf8
Refresh all sensors before returning readings to cpctl
May 19, 2026
d81a9a0
Fix refreshForQuery: start stopped sensors to populate snapshots
May 19, 2026
33e555e
Add WiFi network picker to rule creation modal
May 19, 2026
dc5d4c0
Simplify WiFi rule condition to Connected/Disconnected
May 19, 2026
6a1033b
Fix sensor event subscriptions: add sleep/wake refresh and NetworkLin…
May 19, 2026
d2edaa8
Fix WiFiSensor not detecting disconnect from network
May 19, 2026
3447ec1
Implement issue #540: rename Preferences to Settings, show Dock icon …
May 19, 2026
a374a5d
Fix Run Action flyout menu to use Action+ProfileActionLink architecture
May 19, 2026
5dc85e6
Show all actions in Run Action menu, not just profile-linked ones
May 19, 2026
82f544a
Simplify Run Action menu to flat list of all actions
May 19, 2026
02b07ed
USB device picker: show connected devices by name (fixes #545)
May 19, 2026
632f3d4
USB rule condition: Connected/Disconnected picker instead of negate t…
May 19, 2026
2caf5f8
Refresh USB snapshot on sensor select so picker shows connected devices
May 19, 2026
9386f06
Fix make clean && make run by separating product builds and using rm -rf
May 19, 2026
e6fffa5
Remove freeform USB ID text field from rule creation UI
May 19, 2026
ae10dda
Add GitHub Actions PR build workflow and make bundle target
May 20, 2026
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
38 changes: 38 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: PR Build

on:
pull_request:
branches: [main]

jobs:
build:
runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- name: Cache SPM dependencies
uses: actions/cache@v4
with:
path: .build/checkouts
key: spm-${{ runner.os }}-${{ hashFiles('Package.resolved') }}
restore-keys: |
spm-${{ runner.os }}-

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode.app

- name: Build and assemble bundle
run: make bundle

- name: Zip app bundle
run: |
cd .build/universal/debug
zip -r ControlPlane.zip ControlPlane.app

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ControlPlane-PR${{ github.event.pull_request.number }}
path: .build/universal/debug/ControlPlane.zip
retention-days: 14
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ All new sensors extend `BaseSensor` (in `ControlPlaneSDK`). `BaseSensor` impleme
| `RunningApplicationSensor` | `RunningApplicationSensor` | `com.controlplane.sensors.runningapplication` | Push+Dynamic | Keys are bundle IDs; reading per key → boolean |
| `MountedVolumeSensor` | `MountedVolumeSensor` | `com.controlplane.sensors.mountedvolume` | Push | NSWorkspace mount/unmount; reads `mounted` (strings) + per-volume boolean |
| `ScreenLockSensor` | `ScreenLockSensor` | `com.controlplane.sensors.screenlock` | Push | DistributedNotificationCenter; reads `locked` boolean |
| `USBSensor` | `USBSensor` | `com.controlplane.sensors.usb` | Push+Dynamic | IOKit; keys are `"vendorID:productID"`; reads `devices` strings |
| `USBSensor` | `USBSensor` | `com.controlplane.sensors.usb` | Push+Dynamic | IOKit; keys are lowercase hex `"vvvv:pppp"` (e.g. `"05ac:12a8"`); emits one boolean reading per connected device (label = product name) plus `devices` strings summary |
| `BluetoothSensor` | `BluetoothSensor` | `com.controlplane.sensors.bluetooth` | Push | IOBluetooth; reads `powered`, `devices`, per-MAC boolean |
| `NetworkLinkSensor` | `NetworkLinkSensor` | `com.controlplane.sensors.networklink` | Push | SCDynamicStore; reads per-interface boolean + `activeInterfaces` |
| `IPAddressSensor` | `IPAddressSensor` | `com.controlplane.sensors.ipaddress` | Push | SCDynamicStore; reads `<iface>.ipv4`, `<iface>.ipv6`, `allAddresses` |
Expand Down
32 changes: 25 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ APP_BINARY := $(APP_BUNDLE)/Contents/MacOS/ControlPlane
INFO_PLIST := Resources/ControlPlane-Info.plist
ICON := Resources/AppIcon.icns

.PHONY: all build install run clean
.PHONY: all build install run bundle clean

## Default: build universal binaries and install cpctl
all: build install

## Build for arm64 and x86_64, then lipo into universal binaries
## Build for arm64 and x86_64, then lipo into universal binaries.
## Each product is built with a separate invocation so SPM cannot silently
## skip one when multiple --product flags are passed on some toolchain versions.
build:
$(SWIFT) build -c $(CONFIG) --arch arm64 --product ControlPlane --product cpctl
$(SWIFT) build -c $(CONFIG) --arch x86_64 --product ControlPlane --product cpctl
$(SWIFT) build -c $(CONFIG) --arch arm64 --product ControlPlane
$(SWIFT) build -c $(CONFIG) --arch arm64 --product cpctl
$(SWIFT) build -c $(CONFIG) --arch x86_64 --product ControlPlane
$(SWIFT) build -c $(CONFIG) --arch x86_64 --product cpctl
@mkdir -p $(UNIV_DIR)
lipo -create $(BIN_ARM)/ControlPlane $(BIN_X86)/ControlPlane -output $(UNIV_DIR)/ControlPlane
lipo -create $(BIN_ARM)/cpctl $(BIN_X86)/cpctl -output $(UNIV_DIR)/cpctl
Expand Down Expand Up @@ -49,7 +53,21 @@ run: build install
open "$(APP_BUNDLE)"
@echo "ControlPlane running from $(APP_BUNDLE)"

## Remove all build artifacts
## Assemble the app bundle and sign it ad-hoc, without launching.
## Used by CI to produce a downloadable artifact.
bundle: build
@mkdir -p "$(APP_BUNDLE)/Contents/MacOS"
@mkdir -p "$(APP_BUNDLE)/Contents/Resources"
cp $(INFO_PLIST) "$(APP_BUNDLE)/Contents/Info.plist"
cp $(UNIV_DIR)/ControlPlane "$(APP_BINARY)"
cp $(UNIV_DIR)/cpctl "$(APP_BUNDLE)/Contents/MacOS/cpctl"
cp $(ICON) "$(APP_BUNDLE)/Contents/Resources/AppIcon.icns"
codesign --force --deep --sign - --identifier "com.controlplane.app" "$(APP_BUNDLE)"
@echo "Bundle assembled → $(APP_BUNDLE)"

## Remove all build artifacts.
## Uses rm -rf instead of 'swift package clean' to guarantee a truly clean
## state — swift package clean can leave stale SPM metadata that causes the
## next build to produce only some products.
clean:
$(SWIFT) package clean
rm -rf .build/universal
rm -rf .build
Loading
Loading