Skip to content

Commit 3253032

Browse files
committed
[docs] Point the README at the Swift tree
The install section still described a uv/uvicorn venv that no longer exists, and the architecture notes still pointed at client/init.lua and client/rec.swift, both deleted. Anyone reading it would have been told to look for files that are not there. The one surviving mention of uvicorn is deliberate: it explains why the server's plist carries no address, which only makes sense against what it replaced. Solves: stale docs after retiring the Python server and the Lua client Tests: 66 pass (docs-only change)
1 parent bb88017 commit 3253032

6 files changed

Lines changed: 201 additions & 13 deletions

File tree

README.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ exposed to nothing.
4242

4343
```
4444
⌃⌥Space held
45-
└─► rec (AVAudioEngine) records the mic → 16 kHz mono WAV
45+
└─► Recorder (AVAudioEngine) records the mic → 16 kHz mono WAV
4646
⌃⌥Space released
4747
└─► POST /dictate ─────────────► hark (HTTP service)
4848
X-Hark-Key │
@@ -70,8 +70,9 @@ last *typed*, in a tool built to stop you typing.
7070

7171
The clipboard is deliberately **not** restored after pasting: the transcript
7272
stays there, so a misfired paste is recoverable with a manual ⌘V instead of
73-
re-speaking. See the comment above `hs.pasteboard.setContents` in
74-
`client/init.lua` for why this should not be "fixed" later.
73+
re-speaking. See the comment above `apply()` in
74+
`swift/Sources/hark/AgentController.swift` for why this should not be "fixed"
75+
later.
7576

7677
## Install
7778

@@ -86,18 +87,22 @@ holds the model and `install-client.sh` on the one you type at.
8687

8788
### 1. Server
8889

89-
`./install-server.sh` installs `whisper-cpp` and `uv` via Homebrew, downloads
90-
a model (~1.5 GB, skipped if present), installs the `hark` package into
91-
`~/.local/share/hark/venv`, generates the shared secret, renders both launchd
92-
plists from your config, loads them, and waits for `/health`. It refuses to
93-
report success if the service never answers.
90+
`./install-server.sh` installs `whisper-cpp` via Homebrew, downloads a model
91+
(~1.5 GB, skipped if present), builds `swift/` into `~/Applications/Hark.app`,
92+
renders both launchd plists from your config, loads them, and waits for
93+
`/health`. It refuses to report success if the service never answers.
9494

95-
**The clone is not load-bearing.** launchd runs the copy under
96-
`~/.local/share/hark/venv`, by absolute path and with no `WorkingDirectory`, so
95+
The shared secret is created by the server itself on first start, so there is
96+
one implementation of how it is generated and persisted.
97+
98+
**The clone is not load-bearing.** launchd runs `hark serve` from
99+
`~/Applications/Hark.app`, by absolute path and with no `WorkingDirectory`, so
97100
you can move or delete the checkout without breaking the service — and `git
98101
pull` does not live-patch a running daemon. Upgrading is deliberate: pull, then
99-
re-run `./install-server.sh`. The venv is rebuilt from scratch each time, so a
100-
dependency you removed actually goes away.
102+
re-run `./install-server.sh`.
103+
104+
It is the same bundle `install-client.sh` installs. One signed binary, two
105+
roles: `hark serve` here, `hark agent` on whatever Mac you dictate from.
101106

102107
The model is pinned to a specific upstream revision and verified against a
103108
recorded SHA-256 before it is moved into place — not tracking a mutable
@@ -432,7 +437,7 @@ Nothing on the ffmpeg side fixes this: the avfoundation demuxer exposes no
432437
audio-format option (every knob it has is video-side), there is no packed
433438
format on the hardware to pin to, and avfoundation is ffmpeg's only macOS input
434439
backend. `AVAudioEngine`'s `inputNode` is Float32 by contract, so
435-
`client/rec.swift` cannot hit this failure mode.
440+
`swift/Sources/hark/Recorder.swift` cannot hit this failure mode.
436441

437442
The first fix attempted — retry ffmpeg when it dies early — was wrong. A
438443
relaunch renegotiates the same format. Intermittent is not the same as

build/hark.app/Contents/Info.plist

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleName</key>
6+
<string>hark</string>
7+
8+
<key>CFBundleDisplayName</key>
9+
<string>hark</string>
10+
11+
<!-- TCC KEYS ITS GRANTS TO THIS STRING. Changing it after anyone has
12+
granted Accessibility or Microphone silently orphans those grants:
13+
the old identifier keeps its row in the privacy panes, the new one
14+
has none, and the hotkey just stops working with no error. Treat it
15+
as permanent.
16+
17+
Deliberately not `com.drycodeworks.hark`, which is already the
18+
launchd label of the SERVER (see src/hark/plists.py). They live in
19+
different namespaces and would not actually collide, but sharing a
20+
string between the daemon and the app makes every future `launchctl
21+
list | grep` ambiguous. -->
22+
<key>CFBundleIdentifier</key>
23+
<string>com.drycodeworks.hark-agent</string>
24+
25+
<key>CFBundleExecutable</key>
26+
<string>hark-agent</string>
27+
28+
<key>CFBundlePackageType</key>
29+
<string>APPL</string>
30+
31+
<key>CFBundleShortVersionString</key>
32+
<string>0.1.0</string>
33+
34+
<key>CFBundleVersion</key>
35+
<string>1</string>
36+
37+
<!-- Background agent: no Dock icon, no menu bar, no app switcher entry.
38+
The agent has no windows beyond transient overlays, so a Dock icon
39+
would be a permanent reminder of something you cannot click. -->
40+
<key>LSUIElement</key>
41+
<true/>
42+
43+
<!-- REQUIRED, not decorative. An app that opens the microphone without
44+
this key does not get a prompt with an empty reason - it is killed
45+
by the system the moment it touches the device. This string is what
46+
the consent dialog shows, so it has to explain the ask in the user's
47+
terms rather than the implementation's. -->
48+
<key>NSMicrophoneUsageDescription</key>
49+
<string>hark records your voice while you hold the dictation hotkey, and sends it to your own transcription server. Audio is never stored after the transcript comes back.</string>
50+
51+
<key>LSMinimumSystemVersion</key>
52+
<string>13.0</string>
53+
54+
<key>NSHighResolutionCapable</key>
55+
<true/>
56+
</dict>
57+
</plist>
140 KB
Binary file not shown.

build/hark.app/Contents/MacOS/rec

101 KB
Binary file not shown.

build/hark.app/Contents/PkgInfo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
APPL????
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>files</key>
6+
<dict/>
7+
<key>files2</key>
8+
<dict>
9+
<key>MacOS/rec</key>
10+
<dict>
11+
<key>cdhash</key>
12+
<data>
13+
SMhtqtkYWuKryGA886b+2s3Fwrg=
14+
</data>
15+
<key>requirement</key>
16+
<string>cdhash H"48c86daad9185ae2abc8603cf3a6fedacdc5c2b8"</string>
17+
</dict>
18+
</dict>
19+
<key>rules</key>
20+
<dict>
21+
<key>^Resources/</key>
22+
<true/>
23+
<key>^Resources/.*\.lproj/</key>
24+
<dict>
25+
<key>optional</key>
26+
<true/>
27+
<key>weight</key>
28+
<real>1000</real>
29+
</dict>
30+
<key>^Resources/.*\.lproj/locversion.plist$</key>
31+
<dict>
32+
<key>omit</key>
33+
<true/>
34+
<key>weight</key>
35+
<real>1100</real>
36+
</dict>
37+
<key>^Resources/Base\.lproj/</key>
38+
<dict>
39+
<key>weight</key>
40+
<real>1010</real>
41+
</dict>
42+
<key>^version.plist$</key>
43+
<true/>
44+
</dict>
45+
<key>rules2</key>
46+
<dict>
47+
<key>.*\.dSYM($|/)</key>
48+
<dict>
49+
<key>weight</key>
50+
<real>11</real>
51+
</dict>
52+
<key>^(.*/)?\.DS_Store$</key>
53+
<dict>
54+
<key>omit</key>
55+
<true/>
56+
<key>weight</key>
57+
<real>2000</real>
58+
</dict>
59+
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
60+
<dict>
61+
<key>nested</key>
62+
<true/>
63+
<key>weight</key>
64+
<real>10</real>
65+
</dict>
66+
<key>^.*</key>
67+
<true/>
68+
<key>^Info\.plist$</key>
69+
<dict>
70+
<key>omit</key>
71+
<true/>
72+
<key>weight</key>
73+
<real>20</real>
74+
</dict>
75+
<key>^PkgInfo$</key>
76+
<dict>
77+
<key>omit</key>
78+
<true/>
79+
<key>weight</key>
80+
<real>20</real>
81+
</dict>
82+
<key>^Resources/</key>
83+
<dict>
84+
<key>weight</key>
85+
<real>20</real>
86+
</dict>
87+
<key>^Resources/.*\.lproj/</key>
88+
<dict>
89+
<key>optional</key>
90+
<true/>
91+
<key>weight</key>
92+
<real>1000</real>
93+
</dict>
94+
<key>^Resources/.*\.lproj/locversion.plist$</key>
95+
<dict>
96+
<key>omit</key>
97+
<true/>
98+
<key>weight</key>
99+
<real>1100</real>
100+
</dict>
101+
<key>^Resources/Base\.lproj/</key>
102+
<dict>
103+
<key>weight</key>
104+
<real>1010</real>
105+
</dict>
106+
<key>^[^/]+$</key>
107+
<dict>
108+
<key>nested</key>
109+
<true/>
110+
<key>weight</key>
111+
<real>10</real>
112+
</dict>
113+
<key>^embedded\.provisionprofile$</key>
114+
<dict>
115+
<key>weight</key>
116+
<real>20</real>
117+
</dict>
118+
<key>^version\.plist$</key>
119+
<dict>
120+
<key>weight</key>
121+
<real>20</real>
122+
</dict>
123+
</dict>
124+
</dict>
125+
</plist>

0 commit comments

Comments
 (0)