@hasna/recordings 0.2.10 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +350 -35
  2. package/bun.lock +3 -0
  3. package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
  4. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
  5. package/dist/cli/index.js +1728 -320
  6. package/dist/cli/macos-permissions.d.ts +13 -0
  7. package/dist/cli/macos-permissions.d.ts.map +1 -0
  8. package/dist/cli/options.d.ts +12 -0
  9. package/dist/cli/options.d.ts.map +1 -1
  10. package/dist/db/pg-migrations.d.ts.map +1 -1
  11. package/dist/db/recordings.d.ts +2 -1
  12. package/dist/db/recordings.d.ts.map +1 -1
  13. package/dist/db/remote-storage.d.ts +5 -1
  14. package/dist/db/remote-storage.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +628 -147
  18. package/dist/lib/bun-runtime.d.ts +11 -0
  19. package/dist/lib/bun-runtime.d.ts.map +1 -0
  20. package/dist/lib/config.d.ts +8 -1
  21. package/dist/lib/config.d.ts.map +1 -1
  22. package/dist/lib/enhancer.d.ts +10 -2
  23. package/dist/lib/enhancer.d.ts.map +1 -1
  24. package/dist/lib/install-maintenance.d.ts +24 -0
  25. package/dist/lib/install-maintenance.d.ts.map +1 -0
  26. package/dist/lib/machine.d.ts +2 -0
  27. package/dist/lib/machine.d.ts.map +1 -0
  28. package/dist/lib/recorder.d.ts +2 -1
  29. package/dist/lib/recorder.d.ts.map +1 -1
  30. package/dist/lib/recording-create-identity.d.ts +13 -0
  31. package/dist/lib/recording-create-identity.d.ts.map +1 -0
  32. package/dist/lib/release-install-policy.d.ts +31 -0
  33. package/dist/lib/release-install-policy.d.ts.map +1 -0
  34. package/dist/lib/transcriber.d.ts.map +1 -1
  35. package/dist/mcp/index.d.ts.map +1 -1
  36. package/dist/mcp/index.js +977 -194
  37. package/dist/sdk/index.js +5 -1
  38. package/dist/sdk/v1.generated.d.ts +4 -0
  39. package/dist/sdk/v1.generated.d.ts.map +1 -1
  40. package/dist/server/cloud-config.d.ts +9 -0
  41. package/dist/server/cloud-config.d.ts.map +1 -0
  42. package/dist/server/cloud-readiness.d.ts +10 -0
  43. package/dist/server/cloud-readiness.d.ts.map +1 -0
  44. package/dist/server/cloud.d.ts +9 -18
  45. package/dist/server/cloud.d.ts.map +1 -1
  46. package/dist/server/index.js +1654 -304
  47. package/dist/server/migrate-command.d.ts +11 -0
  48. package/dist/server/migrate-command.d.ts.map +1 -0
  49. package/dist/server/openapi.d.ts +22 -0
  50. package/dist/server/openapi.d.ts.map +1 -1
  51. package/dist/server/repo.d.ts +8 -1
  52. package/dist/server/repo.d.ts.map +1 -1
  53. package/dist/server/serve.d.ts +6 -1
  54. package/dist/server/serve.d.ts.map +1 -1
  55. package/dist/server/v1.d.ts.map +1 -1
  56. package/dist/storage.js +515 -80
  57. package/dist/store.d.ts +3 -1
  58. package/dist/store.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +10 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/package.json +7 -4
  63. package/packaging/macos/Empty.entitlements +5 -0
  64. package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
  65. package/packaging/macos/Verifier.entitlements +10 -0
  66. package/packaging/macos/artifact-verifier.sb +33 -0
  67. package/packaging/macos/build_release_pkg.sh +872 -0
  68. package/packaging/macos/managed_bootstrap.sh +623 -0
  69. package/packaging/macos/pkgutil_fingerprint.awk +37 -0
  70. package/packaging/macos/release_lifecycle.ts +463 -0
  71. package/packaging/macos/scripts/postinstall +565 -0
  72. package/packaging/macos/scripts/preinstall +297 -0
  73. package/scripts/build_companion_cli.sh +337 -0
  74. package/scripts/build_native_fs_guard.sh +59 -0
  75. package/scripts/generate-sdk.ts +19 -1
  76. package/scripts/install_macos_app.sh +1922 -77
  77. package/scripts/macos_artifact.ts +5316 -0
  78. package/scripts/migrate.ts +38 -10
  79. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
  80. package/scripts/native/recordings_fs_guard.c +1167 -0
  81. package/scripts/native_fs_guard.ts +158 -0
  82. package/scripts/release-guard.ts +20 -1
  83. package/scripts/resolve_tailscale_cli.sh +389 -0
  84. package/scripts/smoke_macos_app.sh +573 -0
  85. package/src/native/Recordings/App/ContentView.swift +83 -0
  86. package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
  87. package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
  88. package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
  89. package/src/native/Recordings/App/RecordingsApp.swift +367 -44
  90. package/src/native/Recordings/App/RecordingsListView.swift +130 -0
  91. package/src/native/Recordings/App/RecordingsStore.swift +206 -0
  92. package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
  93. package/src/native/Recordings/App/SidebarView.swift +212 -0
  94. package/src/native/Recordings/App/Theme.swift +87 -0
  95. package/src/native/Recordings/Package.resolved +5 -5
  96. package/src/native/Recordings/Package.swift +62 -2
  97. package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
  98. package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
  99. package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
  100. package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
  101. package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
  102. package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
  103. package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
  104. package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
  105. package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
  106. package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
  107. package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
  108. package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
  109. package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
  110. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
  111. package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
  112. package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
  113. package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
  114. package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
  115. package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
  116. package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
  117. package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
  118. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
  119. package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
  120. package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
  121. package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
  122. package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
  123. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
  124. package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
  125. package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
  126. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
  127. package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
  128. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
  129. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
  130. package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
  131. package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
  132. package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
  133. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
  134. package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
  135. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
  136. package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
  137. package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
  138. package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
  139. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
  140. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
  141. package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
  142. package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
  143. package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
  144. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
  145. package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
  146. package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
  147. package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
  148. package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
  149. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
  150. package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
  151. package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
  152. package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
  153. package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
  154. package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
  155. package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
  156. package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
  157. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
  158. package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
  159. package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
  160. package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
  161. package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
  162. package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
  163. package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
  164. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
  165. package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
  166. package/src/native/Recordings/build.sh +1990 -29
  167. package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
@@ -0,0 +1,573 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ umask 077
4
+
5
+ if [ "$#" -ne 2 ]; then
6
+ echo "Usage: $0 <Recordings.app> <explicit absolute Bun executable path>" >&2
7
+ exit 2
8
+ fi
9
+
10
+ HOST_UNAME_EXECUTABLE="/usr/bin/uname"
11
+ SYSTEM_DIRNAME_EXECUTABLE="/usr/bin/dirname"
12
+ SYSTEM_PWD_EXECUTABLE="/bin/pwd"
13
+ SANITIZED_PATH="/usr/bin:/bin:/usr/sbin:/sbin"
14
+
15
+ require_executable() {
16
+ local label="$1"
17
+ local executable="$2"
18
+ case "$executable" in
19
+ /*) ;;
20
+ *)
21
+ echo "$label must be configured as an absolute executable path." >&2
22
+ exit 1
23
+ ;;
24
+ esac
25
+ if [ ! -f "$executable" ] || [ ! -x "$executable" ]; then
26
+ echo "$label is missing or is not an executable file: $executable" >&2
27
+ exit 1
28
+ fi
29
+ }
30
+
31
+ require_executable "HOST_UNAME_EXECUTABLE" "$HOST_UNAME_EXECUTABLE"
32
+ require_executable "SYSTEM_DIRNAME_EXECUTABLE" "$SYSTEM_DIRNAME_EXECUTABLE"
33
+ require_executable "SYSTEM_PWD_EXECUTABLE" "$SYSTEM_PWD_EXECUTABLE"
34
+ HOST_PLATFORM="$("$HOST_UNAME_EXECUTABLE" -s)"
35
+ readonly HOST_PLATFORM
36
+
37
+ if [ "$HOST_PLATFORM" != "Darwin" ] && [ "${RECORDINGS_TEST_SMOKE_ALLOW_NON_DARWIN:-0}" != "1" ]; then
38
+ echo "Recordings.app runtime smoke is only supported on macOS." >&2
39
+ exit 1
40
+ fi
41
+
42
+ select_executable() {
43
+ local system_executable="$1"
44
+ local test_override="${2:-}"
45
+ if [ "$HOST_PLATFORM" = "Darwin" ] || [ -z "$test_override" ]; then
46
+ printf '%s\n' "$system_executable"
47
+ else
48
+ printf '%s\n' "$test_override"
49
+ fi
50
+ }
51
+
52
+ DIRNAME_EXECUTABLE="$(select_executable "$SYSTEM_DIRNAME_EXECUTABLE" "${RECORDINGS_TEST_SMOKE_DIRNAME_EXECUTABLE:-}")"
53
+ PWD_EXECUTABLE="$(select_executable "$SYSTEM_PWD_EXECUTABLE" "${RECORDINGS_TEST_SMOKE_PWD_EXECUTABLE:-}")"
54
+ BASENAME_EXECUTABLE="$(select_executable "/usr/bin/basename" "${RECORDINGS_TEST_SMOKE_BASENAME_EXECUTABLE:-}")"
55
+ ENV_EXECUTABLE="$(select_executable "/usr/bin/env" "${RECORDINGS_TEST_SMOKE_ENV_EXECUTABLE:-}")"
56
+ LSOF_EXECUTABLE="$(select_executable "/usr/sbin/lsof" "${RECORDINGS_TEST_SMOKE_LSOF_EXECUTABLE:-}")"
57
+ KILL_EXECUTABLE="$(select_executable "/bin/kill" "${RECORDINGS_TEST_SMOKE_KILL_EXECUTABLE:-}")"
58
+ MKTEMP_EXECUTABLE="$(select_executable "/usr/bin/mktemp" "${RECORDINGS_TEST_SMOKE_MKTEMP_EXECUTABLE:-}")"
59
+ MV_EXECUTABLE="$(select_executable "/bin/mv" "${RECORDINGS_TEST_SMOKE_MV_EXECUTABLE:-}")"
60
+ OPEN_EXECUTABLE="$(select_executable "/usr/bin/open" "${RECORDINGS_TEST_SMOKE_OPEN_EXECUTABLE:-}")"
61
+ PS_EXECUTABLE="$(select_executable "/bin/ps" "${RECORDINGS_TEST_SMOKE_PS_EXECUTABLE:-}")"
62
+ RM_EXECUTABLE="$(select_executable "/bin/rm" "${RECORDINGS_TEST_SMOKE_RM_EXECUTABLE:-}")"
63
+ SED_EXECUTABLE="$(select_executable "/usr/bin/sed" "${RECORDINGS_TEST_SMOKE_SED_EXECUTABLE:-}")"
64
+ SLEEP_EXECUTABLE="$(select_executable "/bin/sleep" "${RECORDINGS_TEST_SMOKE_SLEEP_EXECUTABLE:-}")"
65
+ TR_EXECUTABLE="$(select_executable "/usr/bin/tr" "${RECORDINGS_TEST_SMOKE_TR_EXECUTABLE:-}")"
66
+
67
+ for executable_spec in \
68
+ "DIRNAME_EXECUTABLE:$DIRNAME_EXECUTABLE" \
69
+ "PWD_EXECUTABLE:$PWD_EXECUTABLE" \
70
+ "BASENAME_EXECUTABLE:$BASENAME_EXECUTABLE" \
71
+ "ENV_EXECUTABLE:$ENV_EXECUTABLE" \
72
+ "LSOF_EXECUTABLE:$LSOF_EXECUTABLE" \
73
+ "KILL_EXECUTABLE:$KILL_EXECUTABLE" \
74
+ "MKTEMP_EXECUTABLE:$MKTEMP_EXECUTABLE" \
75
+ "MV_EXECUTABLE:$MV_EXECUTABLE" \
76
+ "OPEN_EXECUTABLE:$OPEN_EXECUTABLE" \
77
+ "PS_EXECUTABLE:$PS_EXECUTABLE" \
78
+ "RM_EXECUTABLE:$RM_EXECUTABLE" \
79
+ "SED_EXECUTABLE:$SED_EXECUTABLE" \
80
+ "SLEEP_EXECUTABLE:$SLEEP_EXECUTABLE" \
81
+ "TR_EXECUTABLE:$TR_EXECUTABLE"; do
82
+ require_executable "${executable_spec%%:*}" "${executable_spec#*:}"
83
+ done
84
+
85
+ BUN_EXECUTABLE="$2"
86
+ require_bun_executable() {
87
+ local executable="$1"
88
+ require_executable "BUN_EXECUTABLE" "$executable"
89
+ if ! "$ENV_EXECUTABLE" -i \
90
+ HOME="/tmp" \
91
+ PATH="$SANITIZED_PATH" \
92
+ "$executable" -e '
93
+ import { realpathSync, statSync } from "node:fs";
94
+ const expected = process.argv[1];
95
+ const actual = process.execPath;
96
+ if (!expected || realpathSync(actual) !== realpathSync(expected)) process.exit(66);
97
+ if (!statSync(actual).isFile()) process.exit(66);
98
+ if (!/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(Bun.version)) process.exit(66);
99
+ ' "$executable"; then
100
+ echo "BUN_EXECUTABLE did not identify the Bun executable that actually ran." >&2
101
+ exit 1
102
+ fi
103
+ }
104
+ require_bun_executable "$BUN_EXECUTABLE"
105
+
106
+ HOME_DIRECTORY="${HOME:-}"
107
+ case "$HOME_DIRECTORY" in
108
+ /*) ;;
109
+ *)
110
+ echo "Recordings.app runtime smoke requires an absolute HOME path." >&2
111
+ exit 1
112
+ ;;
113
+ esac
114
+ if [ ! -d "$HOME_DIRECTORY" ]; then
115
+ echo "Recordings.app runtime smoke HOME is not a directory: $HOME_DIRECTORY" >&2
116
+ exit 1
117
+ fi
118
+
119
+ APP_PARENT="$(cd -P "$("$DIRNAME_EXECUTABLE" "$1")" && "$PWD_EXECUTABLE" -P)"
120
+ APP_PATH="$APP_PARENT/$("$BASENAME_EXECUTABLE" "$1")"
121
+ EXECUTABLE="$APP_PATH/Contents/MacOS/Recordings"
122
+ if [ ! -x "$EXECUTABLE" ]; then
123
+ echo "Recordings.app runtime smoke executable is missing: $EXECUTABLE" >&2
124
+ exit 1
125
+ fi
126
+
127
+ WORK_DIR="$($MKTEMP_EXECUTABLE -d /tmp/recordings-runtime-smoke.XXXXXX)"
128
+ SMOKE_MAX_ATTEMPTS=100
129
+ SMOKE_COMPLETION_ATTEMPTS=200
130
+ SMOKE_CLEANUP_ATTEMPTS=20
131
+ SMOKE_CLEANUP_BIND_ATTEMPTS=3
132
+ SMOKE_FOCUS_EVIDENCE="strict"
133
+ if [ -n "${SSH_CONNECTION:-}" ]; then
134
+ SMOKE_FOCUS_EVIDENCE="ssh-unavailable"
135
+ fi
136
+ SMOKE_PID=""
137
+ SMOKE_PID_START_IDENTITY=""
138
+ SMOKE_APP_PID=""
139
+ SMOKE_APP_PID_START_IDENTITY=""
140
+ SMOKE_APP_IDENTITIES_BEFORE_LAUNCH=""
141
+ SMOKE_ACK_PATH=""
142
+
143
+ process_start_identity() {
144
+ local pid="$1"
145
+ "$PS_EXECUTABLE" -o lstart= -p "$pid" 2>/dev/null | "$TR_EXECUTABLE" -d '\n'
146
+ }
147
+
148
+ process_has_exact_executable() {
149
+ local pid="$1"
150
+ local expected_executable="$2"
151
+ local observed_executable
152
+ while IFS= read -r observed_executable; do
153
+ [ "$observed_executable" = "$expected_executable" ] && return 0
154
+ done < <(
155
+ "$LSOF_EXECUTABLE" -a -p "$pid" -d txt -Fn 2>/dev/null |
156
+ "$SED_EXECUTABLE" -n 's/^n//p'
157
+ )
158
+ return 1
159
+ }
160
+
161
+ capture_process_start_identity() {
162
+ local pid="$1"
163
+ local expected_executable="$2"
164
+ local start_before
165
+ local start_after
166
+ kill -0 "$pid" 2>/dev/null || return 1
167
+ start_before="$(process_start_identity "$pid")"
168
+ [ -n "$start_before" ] || return 1
169
+ process_has_exact_executable "$pid" "$expected_executable" || return 1
170
+ start_after="$(process_start_identity "$pid")"
171
+ [ -n "$start_after" ] && [ "$start_before" = "$start_after" ] || return 1
172
+ printf '%s\n' "$start_before"
173
+ }
174
+
175
+ process_records_for_exact_executable() {
176
+ local expected_executable="$1"
177
+ local required_argument="${2:-}"
178
+ local process_listing
179
+ local pid
180
+ local command
181
+ local start_identity
182
+ if ! process_listing="$("$PS_EXECUTABLE" -axo pid=,command= -ww 2>/dev/null)"; then
183
+ return 1
184
+ fi
185
+ while read -r pid command; do
186
+ [[ "${pid:-}" =~ ^[1-9][0-9]*$ ]] || continue
187
+ case "$command" in
188
+ "$expected_executable"|"$expected_executable "*)
189
+ if [ -n "$required_argument" ]; then
190
+ case " $command " in
191
+ *" $required_argument "*) ;;
192
+ *) continue ;;
193
+ esac
194
+ fi
195
+ if start_identity="$(capture_process_start_identity "$pid" "$expected_executable")"; then
196
+ printf '%s\t%s\n' "$pid" "$start_identity"
197
+ fi
198
+ ;;
199
+ esac
200
+ done <<< "$process_listing"
201
+ }
202
+
203
+ process_identity_existed_before_launch() {
204
+ local candidate_pid="$1"
205
+ local candidate_start_identity="$2"
206
+ local pid
207
+ local start_identity
208
+ while IFS=$'\t' read -r pid start_identity; do
209
+ if [ "$pid" = "$candidate_pid" ] && [ "$start_identity" = "$candidate_start_identity" ]; then
210
+ return 0
211
+ fi
212
+ done <<< "$SMOKE_APP_IDENTITIES_BEFORE_LAUNCH"
213
+ return 1
214
+ }
215
+
216
+ bind_new_exact_app_process() {
217
+ local current_records
218
+ local pid
219
+ local start_identity
220
+ local candidate_pid=""
221
+ local candidate_start_identity=""
222
+ local candidate_count=0
223
+ [ -z "$SMOKE_APP_PID" ] || return 0
224
+ current_records="$(
225
+ process_records_for_exact_executable "$EXECUTABLE" "$SMOKE_ACK_PATH"
226
+ )" || return 1
227
+ while IFS=$'\t' read -r pid start_identity; do
228
+ [ -n "$pid" ] || continue
229
+ process_identity_existed_before_launch "$pid" "$start_identity" && continue
230
+ candidate_pid="$pid"
231
+ candidate_start_identity="$start_identity"
232
+ candidate_count=$((candidate_count + 1))
233
+ done <<< "$current_records"
234
+ [ "$candidate_count" -eq 1 ] || return 1
235
+ SMOKE_APP_PID="$candidate_pid"
236
+ SMOKE_APP_PID_START_IDENTITY="$candidate_start_identity"
237
+ }
238
+
239
+ write_atomic_text_file() {
240
+ local destination="$1"
241
+ local contents="$2"
242
+ local temporary="${destination}.tmp.$$"
243
+ printf '%s\n' "$contents" > "$temporary"
244
+ "$MV_EXECUTABLE" -f "$temporary" "$destination"
245
+ }
246
+
247
+ wait_for_process_exit() {
248
+ local pid="$1"
249
+ local attempts="$2"
250
+ local attempt
251
+ for ((attempt = 0; attempt < attempts; attempt++)); do
252
+ kill -0 "$pid" 2>/dev/null || return 0
253
+ "$SLEEP_EXECUTABLE" 0.1
254
+ done
255
+ ! kill -0 "$pid" 2>/dev/null
256
+ }
257
+
258
+ terminate_verified_process() {
259
+ local label="$1"
260
+ local pid="$2"
261
+ local expected_executable="$3"
262
+ local expected_start_identity="$4"
263
+ local rechecked_start_identity
264
+
265
+ kill -0 "$pid" 2>/dev/null || return 0
266
+ if ! rechecked_start_identity="$(capture_process_start_identity "$pid" "$expected_executable")" ||
267
+ [ "$rechecked_start_identity" != "$expected_start_identity" ]; then
268
+ echo "Refusing to signal ${label}: its process identity changed." >&2
269
+ return 1
270
+ fi
271
+
272
+ "$KILL_EXECUTABLE" -TERM "$pid" 2>/dev/null || true
273
+ wait_for_process_exit "$pid" "$SMOKE_CLEANUP_ATTEMPTS" && return 0
274
+
275
+ if ! rechecked_start_identity="$(capture_process_start_identity "$pid" "$expected_executable")" ||
276
+ [ "$rechecked_start_identity" != "$expected_start_identity" ]; then
277
+ echo "Refusing to force-terminate ${label}: its process identity changed." >&2
278
+ return 1
279
+ fi
280
+ "$KILL_EXECUTABLE" -KILL "$pid" 2>/dev/null || true
281
+ if ! wait_for_process_exit "$pid" "$SMOKE_CLEANUP_ATTEMPTS"; then
282
+ echo "Could not reap verified ${label} process ${pid}." >&2
283
+ return 1
284
+ fi
285
+ }
286
+
287
+ cleanup() {
288
+ local exit_status=$?
289
+ local binding_attempt
290
+ trap - EXIT
291
+ if [ -n "$SMOKE_ACK_PATH" ] && [ ! -e "$SMOKE_ACK_PATH" ]; then
292
+ write_atomic_text_file "$SMOKE_ACK_PATH" "cleanup-$$" 2>/dev/null || true
293
+ fi
294
+ if [ -n "$SMOKE_PID" ]; then
295
+ wait_for_process_exit "$SMOKE_PID" "$SMOKE_CLEANUP_ATTEMPTS" || true
296
+ if [ -z "$SMOKE_APP_PID" ]; then
297
+ for ((binding_attempt = 0; binding_attempt < SMOKE_CLEANUP_BIND_ATTEMPTS; binding_attempt++)); do
298
+ bind_new_exact_app_process && break
299
+ "$SLEEP_EXECUTABLE" 0.1
300
+ done
301
+ fi
302
+ if [ -n "$SMOKE_APP_PID" ] && [ -n "$SMOKE_APP_PID_START_IDENTITY" ]; then
303
+ terminate_verified_process \
304
+ "Recordings.app" \
305
+ "$SMOKE_APP_PID" \
306
+ "$EXECUTABLE" \
307
+ "$SMOKE_APP_PID_START_IDENTITY" || true
308
+ fi
309
+ if ! wait_for_process_exit "$SMOKE_PID" "$SMOKE_CLEANUP_ATTEMPTS" &&
310
+ [ -n "$SMOKE_PID_START_IDENTITY" ]; then
311
+ terminate_verified_process \
312
+ "open -W wrapper" \
313
+ "$SMOKE_PID" \
314
+ "$OPEN_EXECUTABLE" \
315
+ "$SMOKE_PID_START_IDENTITY" || true
316
+ fi
317
+ fi
318
+ if [ -n "$SMOKE_PID" ] && ! kill -0 "$SMOKE_PID" 2>/dev/null; then
319
+ wait "$SMOKE_PID" 2>/dev/null || true
320
+ fi
321
+ "$RM_EXECUTABLE" -rf "$WORK_DIR"
322
+ return "$exit_status"
323
+ }
324
+ trap cleanup EXIT
325
+
326
+ run_smoke() {
327
+ local mode="$1"
328
+ local output="$WORK_DIR/${mode}.json"
329
+ local acknowledgement="$WORK_DIR/${mode}.ack"
330
+ local completion="$WORK_DIR/${mode}.completion.json"
331
+ local log="$WORK_DIR/${mode}.log"
332
+ local attempt
333
+ local challenge
334
+ local completion_attempt
335
+ local rechecked_start_identity
336
+ local result_pid
337
+ local result_pid_start_identity
338
+ local wrapper_status
339
+ local -a arguments=(
340
+ --runtime-smoke "$mode"
341
+ --runtime-smoke-output "$output"
342
+ --runtime-smoke-ack "$acknowledgement"
343
+ --runtime-smoke-completion "$completion"
344
+ )
345
+ if [ "$mode" = "permission-helper" ]; then
346
+ arguments=(--request-permissions "${arguments[@]}")
347
+ fi
348
+
349
+ SMOKE_PID=""
350
+ SMOKE_PID_START_IDENTITY=""
351
+ SMOKE_APP_PID=""
352
+ SMOKE_APP_PID_START_IDENTITY=""
353
+ SMOKE_APP_IDENTITIES_BEFORE_LAUNCH=""
354
+ SMOKE_ACK_PATH="$acknowledgement"
355
+ if ! SMOKE_APP_IDENTITIES_BEFORE_LAUNCH="$(
356
+ process_records_for_exact_executable "$EXECUTABLE"
357
+ )"; then
358
+ echo "Recordings.app runtime smoke ${mode} could not inventory exact app processes before launch." >&2
359
+ return 1
360
+ fi
361
+
362
+ "$ENV_EXECUTABLE" -i \
363
+ HOME="$HOME_DIRECTORY" \
364
+ PATH="$SANITIZED_PATH" \
365
+ TMPDIR="$WORK_DIR" \
366
+ "$OPEN_EXECUTABLE" -n -W "$APP_PATH" --args "${arguments[@]}" >"$log" 2>&1 &
367
+ SMOKE_PID=$!
368
+ SMOKE_PID_START_IDENTITY="$(
369
+ capture_process_start_identity "$SMOKE_PID" "$OPEN_EXECUTABLE"
370
+ )" || SMOKE_PID_START_IDENTITY=""
371
+
372
+ for ((attempt = 0; attempt < SMOKE_MAX_ATTEMPTS; attempt++)); do
373
+ if [ -z "$SMOKE_APP_PID" ]; then
374
+ bind_new_exact_app_process || true
375
+ fi
376
+ if [ -f "$output" ]; then
377
+ result_pid="$("$ENV_EXECUTABLE" -i HOME="$HOME_DIRECTORY" PATH="$SANITIZED_PATH" TMPDIR="$WORK_DIR" \
378
+ "$BUN_EXECUTABLE" -e 'const value = await Bun.file(process.argv[1]).json(); console.log(value.processIdentifier)' "$output")"
379
+ if ! [[ "$result_pid" =~ ^[1-9][0-9]*$ ]] || ! kill -0 "$result_pid" 2>/dev/null; then
380
+ echo "Recordings.app runtime smoke ${mode} reported a process that is not running." >&2
381
+ return 1
382
+ fi
383
+ if ! result_pid_start_identity="$(
384
+ capture_process_start_identity "$result_pid" "$EXECUTABLE"
385
+ )"; then
386
+ echo "Recordings.app runtime smoke ${mode} reported a process outside the exact app path." >&2
387
+ return 1
388
+ fi
389
+ if process_identity_existed_before_launch "$result_pid" "$result_pid_start_identity"; then
390
+ echo "Recordings.app runtime smoke ${mode} reported an app process that predated this launch." >&2
391
+ return 1
392
+ fi
393
+ if [ -n "$SMOKE_APP_PID" ] && {
394
+ [ "$result_pid" != "$SMOKE_APP_PID" ] ||
395
+ [ "$result_pid_start_identity" != "$SMOKE_APP_PID_START_IDENTITY" ];
396
+ }; then
397
+ echo "Recordings.app runtime smoke ${mode} evidence did not match the exact launched app process." >&2
398
+ return 1
399
+ fi
400
+ SMOKE_APP_PID="$result_pid"
401
+ SMOKE_APP_PID_START_IDENTITY="$result_pid_start_identity"
402
+ if [ -z "$SMOKE_PID_START_IDENTITY" ]; then
403
+ SMOKE_PID_START_IDENTITY="$(
404
+ capture_process_start_identity "$SMOKE_PID" "$OPEN_EXECUTABLE"
405
+ )" || SMOKE_PID_START_IDENTITY=""
406
+ fi
407
+ break
408
+ fi
409
+ if ! kill -0 "$SMOKE_PID" 2>/dev/null; then
410
+ echo "Recordings.app runtime smoke ${mode} exited without evidence." >&2
411
+ "$SED_EXECUTABLE" -n '1,120p' "$log" >&2
412
+ return 1
413
+ fi
414
+ "$SLEEP_EXECUTABLE" 0.1
415
+ done
416
+ if [ ! -f "$output" ]; then
417
+ echo "Recordings.app runtime smoke ${mode} timed out." >&2
418
+ "$SED_EXECUTABLE" -n '1,120p' "$log" >&2
419
+ return 1
420
+ fi
421
+ if [ -z "$SMOKE_APP_PID" ]; then
422
+ echo "Recordings.app runtime smoke ${mode} did not bind evidence to the exact app process." >&2
423
+ return 1
424
+ fi
425
+
426
+ "$ENV_EXECUTABLE" -i \
427
+ HOME="$HOME_DIRECTORY" \
428
+ PATH="$SANITIZED_PATH" \
429
+ TMPDIR="$WORK_DIR" \
430
+ SMOKE_MODE="$mode" \
431
+ SMOKE_FOCUS_EVIDENCE="$SMOKE_FOCUS_EVIDENCE" \
432
+ EXPECTED_HELPER="$APP_PATH/Contents/Helpers/recordings" \
433
+ "$BUN_EXECUTABLE" -e '
434
+ const path = process.argv[1];
435
+ const mode = process.env.SMOKE_MODE;
436
+ const result = await Bun.file(path).json();
437
+ const fail = (message) => { throw new Error(`${mode}: ${message}`); };
438
+ let focusEvidenceStatus = "available";
439
+ if (result.mode !== mode) fail("mode mismatch");
440
+ if (result.globalHandlersInstalled !== false) fail("global handlers were installed");
441
+ if (result.permissionRequestsStarted !== 0) fail("permission request path ran");
442
+ if (mode === "normal") {
443
+ if (result.menuBarSurfaceCount !== 1) fail("expected exactly one menu-bar surface");
444
+ if (result.accessibilityMenuBarItemCount !== 1) {
445
+ fail(`expected one accessible menu-bar item, got ${result.accessibilityMenuBarItemCount}`);
446
+ }
447
+ if (result.accessibilityObservationStatus !== "available") {
448
+ fail(`normal menu-bar AX observation was ${result.accessibilityObservationStatus}`);
449
+ }
450
+ if (!result.accessibilityMenuBarLabels.some((label) => label.includes("transcribing"))) {
451
+ fail(`accessible menu-bar label did not render transcribing state: ${JSON.stringify(result.accessibilityMenuBarLabels)}`);
452
+ }
453
+ const expected = ["Recordings", "Recordings, recording", "Recordings, transcribing"];
454
+ if (JSON.stringify(result.renderedStatusLabels) !== JSON.stringify(expected)) {
455
+ fail(`unexpected status labels ${JSON.stringify(result.renderedStatusLabels)}`);
456
+ }
457
+ if (result.windowCreationCount !== 1 || result.windowActivationCount !== 2) {
458
+ fail("retained-window activation path was not exercised twice");
459
+ }
460
+ if (result.retainedWindowReused !== true) fail("main window was not retained");
461
+ if (result.applicationActivationPolicy !== 0) fail("main window did not set regular activation policy");
462
+ if (!result.mainWindowIsVisible || !result.mainWindowCanBecomeKey) {
463
+ fail("retained main window was not visible and capable of becoming key");
464
+ }
465
+ if (!result.applicationIsActive || !result.mainWindowIsKey) {
466
+ if (process.env.SMOKE_FOCUS_EVIDENCE !== "ssh-unavailable") {
467
+ fail("Open Recordings did not make the retained window active and key");
468
+ }
469
+ focusEvidenceStatus = "ssh-unavailable";
470
+ }
471
+ } else if (mode === "resolver") {
472
+ if (result.menuBarSurfaceCount !== 0 || result.globalHandlersInstalled !== false) {
473
+ fail("resolver smoke installed UI surfaces or global handlers");
474
+ }
475
+ if (result.resolvedCompanionPath !== process.env.EXPECTED_HELPER) {
476
+ fail(`resolver selected ${JSON.stringify(result.resolvedCompanionPath)}`);
477
+ }
478
+ if (result.companionCapabilitiesPassed !== true) {
479
+ fail("packaged helper capability contract failed");
480
+ }
481
+ } else {
482
+ if (result.menuBarSurfaceCount !== 0) fail("permission/helper smoke inserted a menu-bar surface");
483
+ if (result.accessibilityObservationStatus !== "absent") {
484
+ fail(`permission/helper AX observation was ${result.accessibilityObservationStatus}`);
485
+ }
486
+ if (result.accessibilityMenuBarItemCount !== 0 || result.accessibilityMenuBarLabels.length !== 0) {
487
+ fail("permission/helper smoke exposed an accessible menu-bar surface");
488
+ }
489
+ if (result.renderedStatusLabels.length !== 0) fail("permission/helper smoke rendered a status label");
490
+ if (result.windowCreationCount !== 0 || result.windowActivationCount !== 0) {
491
+ fail("permission/helper smoke created or activated a window");
492
+ }
493
+ if (result.mainWindowIsVisible || result.mainWindowIsKey) {
494
+ fail("permission/helper smoke exposed a main window");
495
+ }
496
+ }
497
+ console.log(JSON.stringify({
498
+ event: "recordings_runtime_smoke_evidence",
499
+ mode,
500
+ focusEvidenceStatus: mode === "normal" ? focusEvidenceStatus : "not-applicable",
501
+ applicationIsActive: result.applicationIsActive,
502
+ mainWindowIsKey: result.mainWindowIsKey,
503
+ }));
504
+ ' "$output"
505
+
506
+ if [ -e "$completion" ]; then
507
+ echo "Recordings.app runtime smoke ${mode} produced completion before the challenge." >&2
508
+ return 1
509
+ fi
510
+ challenge="$("$ENV_EXECUTABLE" -i HOME="$HOME_DIRECTORY" PATH="$SANITIZED_PATH" TMPDIR="$WORK_DIR" \
511
+ "$BUN_EXECUTABLE" -e 'console.log(crypto.randomUUID())')"
512
+ if ! rechecked_start_identity="$(
513
+ capture_process_start_identity "$result_pid" "$EXECUTABLE"
514
+ )" || [ "$rechecked_start_identity" != "$result_pid_start_identity" ]; then
515
+ echo "Recordings.app runtime smoke ${mode} process identity changed before completion challenge." >&2
516
+ return 1
517
+ fi
518
+ write_atomic_text_file "$acknowledgement" "$challenge"
519
+
520
+ for ((completion_attempt = 0; completion_attempt < SMOKE_COMPLETION_ATTEMPTS; completion_attempt++)); do
521
+ kill -0 "$SMOKE_PID" 2>/dev/null || break
522
+ "$SLEEP_EXECUTABLE" 0.1
523
+ done
524
+ if kill -0 "$SMOKE_PID" 2>/dev/null; then
525
+ echo "Recordings.app runtime smoke ${mode} completion handshake timed out." >&2
526
+ return 1
527
+ fi
528
+ if wait "$SMOKE_PID" 2>/dev/null; then
529
+ wrapper_status=0
530
+ else
531
+ wrapper_status=$?
532
+ fi
533
+ SMOKE_PID=""
534
+ SMOKE_PID_START_IDENTITY=""
535
+ SMOKE_APP_PID=""
536
+ SMOKE_APP_PID_START_IDENTITY=""
537
+ if [ "$wrapper_status" -ne 0 ]; then
538
+ echo "Recordings.app runtime smoke ${mode} open -W wrapper exited unsuccessfully (${wrapper_status})." >&2
539
+ "$SED_EXECUTABLE" -n '1,120p' "$log" >&2
540
+ return 1
541
+ fi
542
+
543
+ if ! "$ENV_EXECUTABLE" -i \
544
+ HOME="$HOME_DIRECTORY" \
545
+ PATH="$SANITIZED_PATH" \
546
+ TMPDIR="$WORK_DIR" \
547
+ SMOKE_CHALLENGE="$challenge" \
548
+ SMOKE_MODE="$mode" \
549
+ SMOKE_PROCESS_IDENTIFIER="$result_pid" \
550
+ "$BUN_EXECUTABLE" -e '
551
+ try {
552
+ const response = await Bun.file(process.argv[1]).json();
553
+ if (response.challenge !== process.env.SMOKE_CHALLENGE) throw new Error("challenge mismatch");
554
+ if (response.mode !== process.env.SMOKE_MODE) throw new Error("mode mismatch");
555
+ if (response.processIdentifier !== Number(process.env.SMOKE_PROCESS_IDENTIFIER)) {
556
+ throw new Error("process identifier mismatch");
557
+ }
558
+ } catch (error) {
559
+ console.error(error instanceof Error ? error.message : String(error));
560
+ process.exit(1);
561
+ }
562
+ ' "$completion"; then
563
+ echo "Recordings.app runtime smoke ${mode} did not provide a valid completion response." >&2
564
+ return 1
565
+ fi
566
+
567
+ SMOKE_ACK_PATH=""
568
+ }
569
+
570
+ run_smoke normal
571
+ run_smoke permission-helper
572
+ run_smoke resolver
573
+ echo "Recordings.app runtime smoke passed: menu bar, retained window, helper isolation, and packaged resolver."
@@ -0,0 +1,83 @@
1
+ import SwiftUI
2
+ import RecordingsLib
3
+
4
+ /// Root layout: a narrow violet Liquid-Glass sidebar on the left, and ONE continuous canvas
5
+ /// on the right — a compact header line, then either the Record workspace (default) or the
6
+ /// recordings library (list | detail), separated only by hairline dividers. No boxed panels.
7
+ struct ContentView: View {
8
+ @ObservedObject var store: RecordingsStore
9
+ @Environment(\.colorScheme) private var colorScheme
10
+
11
+ var body: some View {
12
+ HStack(spacing: 0) {
13
+ SidebarView(store: store)
14
+ .frame(width: Theme.sidebarWidth)
15
+ .background(Theme.sidebarGradient(colorScheme).ignoresSafeArea())
16
+
17
+ VStack(spacing: 0) {
18
+ HeaderBar(store: store)
19
+ Divider().opacity(0.5)
20
+
21
+ switch store.pane {
22
+ case .record:
23
+ RecordWorkspaceView(store: store)
24
+ case .library:
25
+ HSplitView {
26
+ RecordingsListView(store: store)
27
+ .frame(minWidth: 280, idealWidth: 340)
28
+ RecordingDetailView(store: store)
29
+ .frame(minWidth: 420)
30
+ }
31
+ }
32
+ }
33
+ .background(Theme.canvas(colorScheme))
34
+ }
35
+ .frame(minWidth: 940, minHeight: 620)
36
+ .ignoresSafeArea(.container, edges: .top)
37
+ .onAppear { store.loadLibrary() }
38
+ .alert("Recordings Error", isPresented: Binding(
39
+ get: { store.operationError != nil },
40
+ set: { if !$0 { store.operationError = nil } }
41
+ )) {
42
+ Button("OK", role: .cancel) { store.operationError = nil }
43
+ } message: {
44
+ Text(store.operationError ?? "The operation failed.")
45
+ }
46
+ }
47
+ }
48
+
49
+ /// Compact header line: "12 recordings · Updated 3m ago" + actions.
50
+ private struct HeaderBar: View {
51
+ @ObservedObject var store: RecordingsStore
52
+
53
+ var body: some View {
54
+ HStack(spacing: 6) {
55
+ Text("\(store.library.count) recording\(store.library.count == 1 ? "" : "s")")
56
+ if let updated = store.lastUpdated {
57
+ Text("·")
58
+ Text("Updated \(updated.relativeDescription)")
59
+ }
60
+ Spacer()
61
+
62
+ Button { store.loadLibrary() } label: {
63
+ Image(systemName: "arrow.clockwise")
64
+ }
65
+ .buttonStyle(.plain)
66
+ .help("Refresh library")
67
+
68
+ Button {
69
+ store.pane = .record
70
+ } label: {
71
+ Image(systemName: "mic.circle.fill")
72
+ }
73
+ .buttonStyle(.plain)
74
+ .foregroundStyle(Theme.accent)
75
+ .help("New recording")
76
+ }
77
+ .font(.system(.subheadline, design: .rounded))
78
+ .foregroundStyle(.secondary)
79
+ .padding(.horizontal, 18)
80
+ .padding(.top, 20)
81
+ .padding(.bottom, 10)
82
+ }
83
+ }