@hasna/recordings 0.3.13 → 0.4.0

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 (105) hide show
  1. package/README.md +223 -37
  2. package/bun.lock +37 -38
  3. package/dist/cli/index.js +1884 -615
  4. package/dist/cli/macos-shortcut.d.ts +2 -2
  5. package/dist/db/database.d.ts.map +1 -1
  6. package/dist/db/pg-migrations.d.ts.map +1 -1
  7. package/dist/db/recordings.d.ts +6 -0
  8. package/dist/db/recordings.d.ts.map +1 -1
  9. package/dist/http/client.d.ts +183 -22
  10. package/dist/http/client.d.ts.map +1 -1
  11. package/dist/index.d.ts +3 -3
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2858 -2288
  14. package/dist/lib/audio-artifact-storage.d.ts +98 -0
  15. package/dist/lib/audio-artifact-storage.d.ts.map +1 -0
  16. package/dist/lib/capture-probe.d.ts +3 -3
  17. package/dist/lib/capture-probe.d.ts.map +1 -1
  18. package/dist/lib/config.d.ts.map +1 -1
  19. package/dist/lib/local-opt-in.d.ts +132 -0
  20. package/dist/lib/local-opt-in.d.ts.map +1 -0
  21. package/dist/lib/macos-bundle.d.ts +1 -1
  22. package/dist/lib/persistence-probe.d.ts +56 -17
  23. package/dist/lib/persistence-probe.d.ts.map +1 -1
  24. package/dist/lib/transcriber.d.ts.map +1 -1
  25. package/dist/mcp/index.d.ts.map +1 -1
  26. package/dist/mcp/index.js +1689 -456
  27. package/dist/sdk/index.d.ts +13 -5
  28. package/dist/sdk/index.d.ts.map +1 -1
  29. package/dist/sdk/index.js +904 -1
  30. package/dist/sdk/resolve.d.ts +53 -0
  31. package/dist/sdk/resolve.d.ts.map +1 -0
  32. package/dist/sdk/v1.generated.d.ts +6 -0
  33. package/dist/sdk/v1.generated.d.ts.map +1 -1
  34. package/dist/server/client-ip.d.ts +39 -0
  35. package/dist/server/client-ip.d.ts.map +1 -0
  36. package/dist/server/cloud-config.d.ts +12 -0
  37. package/dist/server/cloud-config.d.ts.map +1 -1
  38. package/dist/server/cloud-readiness.d.ts.map +1 -1
  39. package/dist/server/cloud.d.ts +20 -2
  40. package/dist/server/cloud.d.ts.map +1 -1
  41. package/dist/server/index.js +566 -284
  42. package/dist/server/migrate-command.d.ts +1 -1
  43. package/dist/server/migrate-command.d.ts.map +1 -1
  44. package/dist/server/openapi.d.ts +21 -0
  45. package/dist/server/openapi.d.ts.map +1 -1
  46. package/dist/server/repo.d.ts.map +1 -1
  47. package/dist/server/serve.d.ts.map +1 -1
  48. package/dist/storage.d.ts +2 -2
  49. package/dist/storage.d.ts.map +1 -1
  50. package/dist/storage.js +1334 -871
  51. package/dist/store.d.ts +12 -3
  52. package/dist/store.d.ts.map +1 -1
  53. package/dist/types/index.d.ts +15 -0
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/package.json +9 -8
  56. package/packaging/macos/build_release_pkg.sh +3 -3
  57. package/packaging/macos/managed_bootstrap.sh +4 -4
  58. package/packaging/macos/scripts/postinstall +2 -2
  59. package/packaging/macos/scripts/preinstall +2 -2
  60. package/scripts/build_companion_cli.sh +7 -1
  61. package/scripts/install_macos_app.sh +27 -24
  62. package/scripts/macos_artifact.ts +20 -16
  63. package/scripts/migrate.ts +4 -20
  64. package/scripts/smoke_macos_app.sh +21 -21
  65. package/src/native/Recordings/App/ContentView.swift +53 -68
  66. package/src/native/Recordings/App/MenuBarStatusView.swift +171 -61
  67. package/src/native/Recordings/App/RecordWorkspaceView.swift +47 -449
  68. package/src/native/Recordings/App/RecorderSettingsView.swift +75 -0
  69. package/src/native/Recordings/App/RecordingDetailView.swift +3 -4
  70. package/src/native/Recordings/App/RecordingsApp.swift +185 -9
  71. package/src/native/Recordings/App/RecordingsListView.swift +60 -112
  72. package/src/native/Recordings/App/RecordingsStore.swift +124 -109
  73. package/src/native/Recordings/App/Theme.swift +73 -61
  74. package/src/native/Recordings/Package.swift +1 -0
  75. package/src/native/Recordings/RecordingsLib/CaptureMonitor.swift +65 -0
  76. package/src/native/Recordings/RecordingsLib/Info.plist +3 -3
  77. package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +75 -21
  78. package/src/native/Recordings/RecordingsLib/Recording.swift +14 -2
  79. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +316 -34
  80. package/src/native/Recordings/RecordingsLib/RecordingGlassBackground.swift +34 -0
  81. package/src/native/Recordings/RecordingsLib/RecordingProvider.swift +102 -0
  82. package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +17 -10
  83. package/src/native/Recordings/RecordingsLib/ServiceAPIConfiguration.swift +107 -0
  84. package/src/native/Recordings/RecordingsLib/ServiceConnectionView.swift +76 -0
  85. package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -149
  86. package/src/native/Recordings/RecordingsLib/SettingsWindowController.swift +48 -0
  87. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +13 -4
  88. package/src/native/Recordings/RecordingsTests/OpenAIAPIKeyStoreTests.swift +43 -38
  89. package/src/native/Recordings/RecordingsTests/RecorderSurfaceTests.swift +60 -0
  90. package/src/native/Recordings/RecordingsTests/RecordingDateTests.swift +26 -0
  91. package/src/native/Recordings/RecordingsTests/RecordingProviderTests.swift +195 -0
  92. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +1 -1
  93. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +30 -0
  94. package/src/native/Recordings/RecordingsTests/ServiceAPIConfigurationTests.swift +75 -0
  95. package/src/native/Recordings/RecordingsTests/SettingsWindowControllerTests.swift +28 -0
  96. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +1 -1
  97. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +2 -2
  98. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +1 -1
  99. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +1 -1
  100. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +1 -1
  101. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +2 -2
  102. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +1 -1
  103. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +2 -2
  104. package/src/native/Recordings/build.sh +6 -6
  105. package/src/native/Recordings/App/SidebarView.swift +0 -212
@@ -253,7 +253,7 @@ struct DurableInstallJournal {
253
253
  let leaf = URL(fileURLWithPath: transactionDirectory).lastPathComponent
254
254
  guard leaf.hasPrefix("transaction-") else { throw InstallJournalError.invalidJournal }
255
255
  let transactionID = String(leaf.dropFirst("transaction-".count))
256
- let expectedCandidate = transactionDirectory + "/candidate/HasnaRecordings.app"
256
+ let expectedCandidate = transactionDirectory + "/candidate/Hasna Recordings.app"
257
257
  let activationPhases: Set<String> = [
258
258
  "prepared", "launch-barrier-pending", "launch-barrier-held",
259
259
  "swap-pending", "swapped", "previous-retaining",
@@ -201,8 +201,8 @@ struct ActivationRecoveryPolicyTests {
201
201
  "Contents/Helpers/recordings": 493,
202
202
  "Contents/Helpers/recordings-update-client": 493
203
203
  },
204
- "candidate_application_path": "/Library/Application Support/Hasna/Recordings/Updates/transaction-00000000-0000-0000-0000-000000000001/candidate/HasnaRecordings.app",
205
- "application_path": "/Applications/HasnaRecordings.app"
204
+ "candidate_application_path": "/Library/Application Support/Hasna/Recordings/Updates/transaction-00000000-0000-0000-0000-000000000001/candidate/Hasna Recordings.app",
205
+ "application_path": "/Applications/Hasna Recordings.app"
206
206
  }
207
207
  """#.utf8)
208
208
  let journal = try JSONDecoder().decode(BrokerInstallJournal.self, from: legacyJSON)
@@ -7,7 +7,7 @@ public enum RecordingsUpdateConstants {
7
7
  public static let rootMaintenanceSupported = false
8
8
  public static let keyRotationSupported = false
9
9
  public static let machServiceName = "com.hasna.recordings.updater"
10
- public static let applicationPath = "/Applications/HasnaRecordings.app"
10
+ public static let applicationPath = "/Applications/Hasna Recordings.app"
11
11
  public static let brokerExecutablePath = "/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
12
12
  public static let stateRoot = "/Library/Application Support/Hasna/Recordings/Updates"
13
13
  public static let trustRoot = "/Library/Application Support/Hasna/Recordings/Trust"
@@ -729,7 +729,7 @@ int recordings_copy_canonical_application_tree(
729
729
  if (entry == NULL) break;
730
730
  if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
731
731
  visible_entries += 1;
732
- if (strcmp(entry->d_name, "HasnaRecordings.app") != 0) only_application = false;
732
+ if (strcmp(entry->d_name, "Hasna Recordings.app") != 0) only_application = false;
733
733
  }
734
734
  int status = errno;
735
735
  closedir(directory);
@@ -739,7 +739,7 @@ int recordings_copy_canonical_application_tree(
739
739
  status = copy_directory_at(
740
740
  verifier_output_directory_descriptor,
741
741
  root_candidate_directory_descriptor,
742
- "HasnaRecordings.app",
742
+ "Hasna Recordings.app",
743
743
  verifier_user_id,
744
744
  &budget
745
745
  );
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- # Build, sign, notarize, and finalize a HasnaRecordings.app artifact.
2
+ # Build, sign, notarize, and finalize a Hasna Recordings.app artifact.
3
3
  # Usage: ./build.sh [debug|local] | ./build.sh release <initial-bootstrap|app-update>
4
4
 
5
5
  set -euo pipefail
@@ -120,11 +120,11 @@ if [ "$VARIANT" = "bar" ]; then
120
120
  fi
121
121
  # Fleet naming rule (knowledge k_msxd5rz3_jfvl3i): every Hasna macOS app is
122
122
  # Hasna<Name>.app with 'Hasna' at the beginning. The bundle is named
123
- # HasnaRecordings.app for BOTH variants (full and bar); the 'bar' is a variant, never
123
+ # Hasna Recordings.app for BOTH variants (full and bar); the 'bar' is a variant, never
124
124
  # a separate app name. Bundle identifier stays com.hasna.recordings so TCC keeps
125
125
  # keying on bundle id + signing identity, and the executable inside the bundle stays
126
126
  # "Recordings".
127
- APP_BUNDLE_NAME="HasnaRecordings.app"
127
+ APP_BUNDLE_NAME="Hasna Recordings.app"
128
128
  readonly APP_BUNDLE_NAME
129
129
  APP_BASENAME="${APP_BUNDLE_NAME%.app}"
130
130
  PLIST_BUDDY="$(select_executable "/usr/libexec/PlistBuddy" "${RECORDINGS_TEST_PLIST_BUDDY_EXECUTABLE:-${PLIST_BUDDY:-}}")"
@@ -1256,7 +1256,7 @@ fi
1256
1256
 
1257
1257
  generate_and_verify_native_fs_guard
1258
1258
 
1259
- echo "Building HasnaRecordings.app ($MODE)..."
1259
+ echo "Building Hasna Recordings.app ($MODE)..."
1260
1260
  if [ "$MODE" = "release" ]; then
1261
1261
  OUTPUT_BUILD_DIR="$BUILD_ROOT/release-output"
1262
1262
  else
@@ -1992,7 +1992,7 @@ if [ "$MODE" = "local" ]; then
1992
1992
  fi
1993
1993
 
1994
1994
  verify_signed_code "$HELPERS/recordings" "Companion CLI"
1995
- verify_signed_code "$APP_DIR" "HasnaRecordings.app"
1995
+ verify_signed_code "$APP_DIR" "Hasna Recordings.app"
1996
1996
  ARTIFACT_BASENAME="${APP_BASENAME}-${VERSION}-macos-${RELEASE_SUBTYPE}"
1997
1997
  NOTARY_ARCHIVE="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-notarization.zip"
1998
1998
  FINAL_ARCHIVE="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.zip"
@@ -2044,7 +2044,7 @@ run_xcrun stapler validate "$APP_DIR"
2044
2044
  run_release_sensitive_tool "$SPCTL_EXECUTABLE" --assess --type execute --verbose=2 "$APP_DIR"
2045
2045
  run_release_sensitive_tool "$SYSPOLICY_CHECK_EXECUTABLE" distribution "$APP_DIR"
2046
2046
  verify_signed_code "$HELPERS/recordings" "Companion CLI"
2047
- verify_signed_code "$APP_DIR" "HasnaRecordings.app"
2047
+ verify_signed_code "$APP_DIR" "Hasna Recordings.app"
2048
2048
  run_codesign --verify --deep --strict --verbose=2 "$APP_DIR"
2049
2049
 
2050
2050
  run_release_sensitive_tool "$DITTO_EXECUTABLE" -c -k --sequesterRsrc --keepParent "$APP_DIR" "$FINAL_ARCHIVE"
@@ -1,212 +0,0 @@
1
- import SwiftUI
2
- import RecordingsLib
3
-
4
- /// Narrow violet sidebar: Record · Library · Projects · Modes · Machines. White text/tint
5
- /// over the gradient; selected rows get a translucent white highlight (Liquid Glass refracts
6
- /// the violet beneath).
7
- struct SidebarView: View {
8
- @ObservedObject var store: RecordingsStore
9
- @Environment(\.colorScheme) private var colorScheme
10
-
11
- @State private var addingProject = false
12
- @State private var newProjectName = ""
13
-
14
- var body: some View {
15
- ScrollView {
16
- VStack(alignment: .leading, spacing: 18) {
17
- workspaceSection
18
- librarySection
19
- projectsSection
20
- if hasAnyMode { modesSection }
21
- if !store.machines.isEmpty { machinesSection }
22
- Spacer(minLength: 8)
23
- }
24
- .padding(14)
25
- .padding(.top, 24)
26
- }
27
- .scrollContentBackground(.hidden)
28
- .foregroundStyle(.white)
29
- .tint(.white)
30
- }
31
-
32
- // MARK: - Workspace
33
-
34
- private var workspaceSection: some View {
35
- section(title: "Workspace") {
36
- Button {
37
- withAnimation(.spring(response: 0.3, dampingFraction: 0.85)) { store.pane = .record }
38
- } label: {
39
- rowLabel(icon: "mic.fill", label: "Record", count: nil,
40
- selected: store.pane == .record, accentDot: store.engine.captureIsActive)
41
- }
42
- .buttonStyle(.plain)
43
- }
44
- }
45
-
46
- // MARK: - Library
47
-
48
- private var librarySection: some View {
49
- section(title: "Library") {
50
- filterRow(.all, icon: "waveform", label: "All Recordings")
51
- if store.count(for: .noProject) > 0 {
52
- filterRow(.noProject, icon: "tray", label: "No Project")
53
- }
54
- }
55
- }
56
-
57
- // MARK: - Projects
58
-
59
- private var projectsSection: some View {
60
- section(title: "Projects", trailing: {
61
- Button { addingProject = true; newProjectName = "" } label: {
62
- Image(systemName: "plus").font(.system(size: 11, weight: .semibold))
63
- }
64
- .buttonStyle(.plain)
65
- .disabled(!store.projectStore.canMutateProjects)
66
- .help("Add Project")
67
- .popover(isPresented: $addingProject, arrowEdge: .trailing) {
68
- VStack(alignment: .leading, spacing: 10) {
69
- Text("New Project").font(.headline)
70
- TextField("Project name", text: $newProjectName)
71
- .textFieldStyle(.roundedBorder).frame(width: 200)
72
- .onSubmit(commitNewProject)
73
- HStack {
74
- Spacer()
75
- Button("Cancel") { addingProject = false }
76
- Button("Add") { commitNewProject() }
77
- .keyboardShortcut(.defaultAction)
78
- .disabled(newProjectName.trimmingCharacters(in: .whitespaces).isEmpty)
79
- }
80
- }
81
- .padding(14)
82
- }
83
- }) {
84
- if store.projects.isEmpty {
85
- Text("No projects")
86
- .font(.caption).foregroundStyle(.white.opacity(0.5)).padding(.leading, 6)
87
- }
88
- ForEach(store.projects) { project in
89
- filterRow(.project(project.id), icon: "folder", label: project.name)
90
- .disabled(!store.projectStore.canMutateProjects)
91
- .contextMenu {
92
- Button("Delete", role: .destructive) {
93
- do {
94
- try store.projectStore.removeProject(id: project.id)
95
- } catch {
96
- store.operationError = store.projectStore.persistenceError ?? error.localizedDescription
97
- }
98
- }
99
- }
100
- }
101
- }
102
- }
103
-
104
- private func commitNewProject() {
105
- let name = newProjectName.trimmingCharacters(in: .whitespacesAndNewlines)
106
- guard !name.isEmpty else { return }
107
- Task {
108
- do {
109
- try await store.projectStore.addProject(name: name)
110
- addingProject = false
111
- newProjectName = ""
112
- } catch {
113
- store.operationError = store.projectStore.persistenceError
114
- ?? (error as? RecordingsCLI.Failure)?.message
115
- ?? error.localizedDescription
116
- }
117
- }
118
- }
119
-
120
- // MARK: - Modes
121
-
122
- private var hasAnyMode: Bool {
123
- store.count(for: .mode("enhanced")) > 0 || store.count(for: .mode("raw")) > 0
124
- }
125
-
126
- private var modesSection: some View {
127
- section(title: "Modes") {
128
- if store.count(for: .mode("enhanced")) > 0 {
129
- filterRow(.mode("enhanced"), icon: "wand.and.stars", label: "Enhanced")
130
- }
131
- if store.count(for: .mode("raw")) > 0 {
132
- filterRow(.mode("raw"), icon: "text.quote", label: "Raw")
133
- }
134
- }
135
- }
136
-
137
- // MARK: - Machines
138
-
139
- private var machinesSection: some View {
140
- section(title: "Machines") {
141
- if store.count(for: .thisMachine) > 0 {
142
- filterRow(.thisMachine, icon: "desktopcomputer", label: "This Mac")
143
- }
144
- ForEach(otherMachines, id: \.self) { machine in
145
- filterRow(.machine(machine), icon: "macpro.gen3.server", label: machine)
146
- }
147
- }
148
- }
149
-
150
- private var otherMachines: [String] {
151
- store.machines.filter { $0 != store.localMachineID }
152
- }
153
-
154
- // MARK: - Building blocks
155
-
156
- @ViewBuilder
157
- private func section<Trailing: View, Content: View>(
158
- title: String,
159
- @ViewBuilder trailing: () -> Trailing = { EmptyView() },
160
- @ViewBuilder content: () -> Content
161
- ) -> some View {
162
- VStack(alignment: .leading, spacing: 6) {
163
- HStack {
164
- Text(title.uppercased())
165
- .font(.caption2.weight(.semibold))
166
- .foregroundStyle(.white.opacity(0.6))
167
- .padding(.leading, 6)
168
- Spacer()
169
- trailing().foregroundStyle(.white.opacity(0.85)).padding(.trailing, 6)
170
- }
171
- content()
172
- }
173
- }
174
-
175
- private func filterRow(_ target: LibraryFilter, icon: String, label: String) -> some View {
176
- let selected = store.pane == .library && store.filter == target
177
- let count = store.count(for: target)
178
- return Button {
179
- withAnimation(.spring(response: 0.32, dampingFraction: 0.82)) {
180
- store.filter = target
181
- store.pane = .library
182
- if !store.visibleRecordings.contains(where: { $0.id == store.selection }) {
183
- store.selection = store.visibleRecordings.first?.id
184
- }
185
- }
186
- } label: {
187
- rowLabel(icon: icon, label: label, count: count > 0 ? count : nil, selected: selected, accentDot: false)
188
- }
189
- .buttonStyle(.plain)
190
- }
191
-
192
- private func rowLabel(icon: String, label: String, count: Int?, selected: Bool, accentDot: Bool) -> some View {
193
- HStack(spacing: 9) {
194
- Image(systemName: icon).font(.system(size: 12, weight: .medium)).frame(width: 16)
195
- Text(label).font(.system(.subheadline, design: .rounded)).lineLimit(1)
196
- Spacer(minLength: 4)
197
- if accentDot {
198
- Circle().fill(Theme.recordRed).frame(width: 7, height: 7)
199
- } else if let count {
200
- Text("\(count)").font(.caption2.monospacedDigit()).foregroundStyle(.white.opacity(0.55))
201
- }
202
- }
203
- .foregroundStyle(.white.opacity(selected ? 1 : 0.85))
204
- .padding(.horizontal, 9).padding(.vertical, 7)
205
- .contentShape(Rectangle())
206
- .background {
207
- if selected {
208
- RoundedRectangle(cornerRadius: Theme.cornerSmall, style: .continuous).fill(.white.opacity(0.22))
209
- }
210
- }
211
- }
212
- }