@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,180 @@
1
+ import Testing
2
+ import Foundation
3
+ @testable import RecordingsLib
4
+
5
+ /// Tests for the CLI bridge layer the full macOS app relies on: decoding the `recordings`
6
+ /// CLI JSON into models and tolerating leading log noise in stdout.
7
+ struct RecordingBridgeTests {
8
+ @Test("native machine identity matches CLI host and explicit fleet override")
9
+ func nativeMachineIdentity() {
10
+ #expect(NativeMachineIdentity.current(environment: [:], hostName: "station05") == "station05")
11
+ #expect(NativeMachineIdentity.current(environment: [:]) == NativeMachineIdentity.posixHostName())
12
+ #expect(NativeMachineIdentity.current(
13
+ environment: ["HASNA_MACHINE_ID": "station05"],
14
+ hostName: "station05.local"
15
+ ) == "station05")
16
+ }
17
+
18
+ @Test("Recording decodes from CLI snake_case JSON")
19
+ func decodeRecording() throws {
20
+ let json = """
21
+ {
22
+ "id": "rec_123",
23
+ "audio_path": "/Users/x/.hasna/recordings/audio/a.wav",
24
+ "raw_text": "hello world",
25
+ "processed_text": "Hello, world.",
26
+ "processing_mode": "enhanced",
27
+ "model_used": "gpt-4o-transcribe",
28
+ "enhancement_model": "gpt-4o",
29
+ "duration_ms": 65000,
30
+ "language": "en",
31
+ "tags": ["demo", "test"],
32
+ "project_id": "proj_1",
33
+ "machine_id": "apple06",
34
+ "created_at": "2026-06-22T10:00:00.000Z"
35
+ }
36
+ """
37
+ let rec = try JSONDecoder().decode(Recording.self, from: Data(json.utf8))
38
+ #expect(rec.id == "rec_123")
39
+ #expect(rec.displayText == "Hello, world.") // prefers processed_text
40
+ #expect(rec.isEnhanced)
41
+ #expect(rec.durationLabel == "1:05")
42
+ #expect(rec.tags == ["demo", "test"])
43
+ #expect(rec.projectId == "proj_1")
44
+ #expect(rec.createdDate != nil)
45
+ }
46
+
47
+ @Test("Recording falls back to raw_text and tolerates missing optional fields")
48
+ func decodeMinimalRecording() throws {
49
+ let json = """
50
+ { "id": "r1", "raw_text": "just raw", "processing_mode": "raw", "duration_ms": 4000, "tags": [], "created_at": "2026-06-22 10:00:00" }
51
+ """
52
+ let rec = try JSONDecoder().decode(Recording.self, from: Data(json.utf8))
53
+ #expect(rec.displayText == "just raw")
54
+ #expect(!rec.isEnhanced)
55
+ #expect(rec.durationLabel == "4s")
56
+ #expect(rec.audioPath == nil)
57
+ #expect(rec.createdDate != nil) // SQLite "yyyy-MM-dd HH:mm:ss" fallback parses
58
+ }
59
+
60
+ @Test("RecordingStats decodes aggregate fields")
61
+ func decodeStats() throws {
62
+ let json = #"{ "total": 12, "raw": 5, "enhanced": 7, "total_duration_ms": 90000, "by_model": {"gpt-4o": 7} }"#
63
+ let stats = try JSONDecoder().decode(RecordingStats.self, from: Data(json.utf8))
64
+ #expect(stats.total == 12)
65
+ #expect(stats.enhanced == 7)
66
+ #expect(stats.totalDurationMs == 90000)
67
+ }
68
+
69
+ @Test("RecordingStats rejects a structurally unrelated JSON log object")
70
+ func statsRejectLogObject() {
71
+ let log = Data(#"{"level":"info","message":"finished"}"#.utf8)
72
+
73
+ #expect(throws: (any Error).self) {
74
+ try JSONDecoder().decode(RecordingStats.self, from: log)
75
+ }
76
+ }
77
+
78
+ @Test("stats decoding ignores valid JSON logs before and after the requested payload")
79
+ func statsDecodeSelectsStructurallyValidPayload() throws {
80
+ let output = """
81
+ {"level":"info","message":"starting"}
82
+ {"total":12,"raw":5,"enhanced":7,"total_duration_ms":90000}
83
+ {"level":"info","message":"finished"}
84
+ """
85
+
86
+ let stats = try RecordingsCLI.decode(RecordingStats.self, from: output)
87
+
88
+ #expect(stats.total == 12)
89
+ #expect(stats.raw == 5)
90
+ #expect(stats.enhanced == 7)
91
+ #expect(stats.totalDurationMs == 90000)
92
+ }
93
+
94
+ @Test("extractJSON strips leading log lines before a JSON array")
95
+ func extractArrayWithLeadingLogs() throws {
96
+ let output = """
97
+ [recordings] opening database
98
+ migrating schema...
99
+ [
100
+ { "id": "a" }
101
+ ]
102
+ """
103
+ let json = RecordingsCLI.extractJSON(from: output)
104
+ #expect(json != nil)
105
+ #expect(json?.hasPrefix("[") == true)
106
+ #expect(json?.hasSuffix("]") == true)
107
+ let data = try #require(json?.data(using: .utf8))
108
+ #expect((try? JSONSerialization.jsonObject(with: data)) is [Any])
109
+ }
110
+
111
+ @Test("extractJSON skips bracket and brace log noise before a decodable payload")
112
+ func extractJSONAfterBracketedNoise() throws {
113
+ let output = """
114
+ [recordings] opening {database}
115
+ worker [1/2] ready
116
+ { "id": "real", "message": "literal [bracket] and {brace}" }
117
+ finished
118
+ """
119
+ let json = try #require(RecordingsCLI.extractJSON(from: output))
120
+ let object = try #require(JSONSerialization.jsonObject(with: Data(json.utf8)) as? [String: String])
121
+ #expect(object["id"] == "real")
122
+ #expect(object["message"] == "literal [bracket] and {brace}")
123
+ }
124
+
125
+ @Test("extractJSON selects the final payload after a valid JSON log line")
126
+ func extractJSONAfterValidJSONLog() throws {
127
+ let output = """
128
+ { "level": "info", "message": "opening database" }
129
+ [ { "id": "recording-1" } ]
130
+ """
131
+ let json = try #require(RecordingsCLI.extractJSON(from: output))
132
+ let array = try #require(JSONSerialization.jsonObject(with: Data(json.utf8)) as? [[String: String]])
133
+ #expect(array == [["id": "recording-1"]])
134
+ }
135
+
136
+ @Test("extractJSON returns object payload when stdout is a JSON object")
137
+ func extractObject() {
138
+ let output = "noise line\n{ \"mode\": \"local\" }\n"
139
+ let json = RecordingsCLI.extractJSON(from: output)
140
+ #expect(json == "{ \"mode\": \"local\" }")
141
+ }
142
+
143
+ @Test("extractJSON returns nil when there is no JSON")
144
+ func extractNone() {
145
+ #expect(RecordingsCLI.extractJSON(from: "ERROR: nothing here") == nil)
146
+ }
147
+
148
+ @Test("list pagination collects every page with Store-compatible offsets")
149
+ func collectAllPages() throws {
150
+ let recordings = ["a", "b", "c"].map {
151
+ Recording(id: $0, rawText: $0)
152
+ }
153
+ var requestedOffsets: [Int] = []
154
+
155
+ let result = try RecordingsCLI.collectAllPages(pageSize: 2) { limit, offset in
156
+ requestedOffsets.append(offset)
157
+ return Array(recordings.dropFirst(offset).prefix(limit))
158
+ }
159
+
160
+ #expect(result.map(\.id) == ["a", "b", "c"])
161
+ #expect(requestedOffsets == [0, 2])
162
+ }
163
+
164
+ @Test("list pagination stops when a backend ignores offsets")
165
+ func collectAllPagesStopsWithoutProgress() throws {
166
+ let repeated = [
167
+ Recording(id: "a", rawText: "a"),
168
+ Recording(id: "b", rawText: "b"),
169
+ ]
170
+ var requestCount = 0
171
+
172
+ let result = try RecordingsCLI.collectAllPages(pageSize: 2) { _, _ in
173
+ requestCount += 1
174
+ return repeated
175
+ }
176
+
177
+ #expect(result.map(\.id) == ["a", "b"])
178
+ #expect(requestCount == 2)
179
+ }
180
+ }