@hasna/recordings 0.2.11 → 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.
- package/README.md +349 -69
- package/bun.lock +3 -0
- package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
- package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
- package/dist/cli/index.js +1728 -320
- package/dist/cli/macos-permissions.d.ts +13 -0
- package/dist/cli/macos-permissions.d.ts.map +1 -0
- package/dist/cli/options.d.ts +12 -0
- package/dist/cli/options.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/recordings.d.ts +2 -1
- package/dist/db/recordings.d.ts.map +1 -1
- package/dist/db/remote-storage.d.ts +5 -1
- package/dist/db/remote-storage.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +628 -147
- package/dist/lib/bun-runtime.d.ts +11 -0
- package/dist/lib/bun-runtime.d.ts.map +1 -0
- package/dist/lib/config.d.ts +8 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/enhancer.d.ts +10 -2
- package/dist/lib/enhancer.d.ts.map +1 -1
- package/dist/lib/install-maintenance.d.ts +24 -0
- package/dist/lib/install-maintenance.d.ts.map +1 -0
- package/dist/lib/machine.d.ts +2 -0
- package/dist/lib/machine.d.ts.map +1 -0
- package/dist/lib/recorder.d.ts +2 -1
- package/dist/lib/recorder.d.ts.map +1 -1
- package/dist/lib/recording-create-identity.d.ts +13 -0
- package/dist/lib/recording-create-identity.d.ts.map +1 -0
- package/dist/lib/release-install-policy.d.ts +31 -0
- package/dist/lib/release-install-policy.d.ts.map +1 -0
- package/dist/lib/transcriber.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +977 -194
- package/dist/sdk/index.js +5 -1
- package/dist/sdk/v1.generated.d.ts +4 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/cloud-config.d.ts +9 -0
- package/dist/server/cloud-config.d.ts.map +1 -0
- package/dist/server/cloud-readiness.d.ts +10 -0
- package/dist/server/cloud-readiness.d.ts.map +1 -0
- package/dist/server/cloud.d.ts +9 -18
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +1654 -304
- package/dist/server/migrate-command.d.ts +11 -0
- package/dist/server/migrate-command.d.ts.map +1 -0
- package/dist/server/openapi.d.ts +22 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/repo.d.ts +8 -1
- package/dist/server/repo.d.ts.map +1 -1
- package/dist/server/serve.d.ts +6 -1
- package/dist/server/serve.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +515 -80
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +7 -4
- package/packaging/macos/Empty.entitlements +5 -0
- package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
- package/packaging/macos/Verifier.entitlements +10 -0
- package/packaging/macos/artifact-verifier.sb +33 -0
- package/packaging/macos/build_release_pkg.sh +872 -0
- package/packaging/macos/managed_bootstrap.sh +623 -0
- package/packaging/macos/pkgutil_fingerprint.awk +37 -0
- package/packaging/macos/release_lifecycle.ts +463 -0
- package/packaging/macos/scripts/postinstall +565 -0
- package/packaging/macos/scripts/preinstall +297 -0
- package/scripts/build_companion_cli.sh +337 -0
- package/scripts/build_native_fs_guard.sh +59 -0
- package/scripts/generate-sdk.ts +19 -1
- package/scripts/install_macos_app.sh +1919 -98
- package/scripts/macos_artifact.ts +5316 -0
- package/scripts/migrate.ts +38 -10
- package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
- package/scripts/native/recordings_fs_guard.c +1167 -0
- package/scripts/native_fs_guard.ts +158 -0
- package/scripts/release-guard.ts +20 -1
- package/scripts/resolve_tailscale_cli.sh +389 -0
- package/scripts/smoke_macos_app.sh +573 -0
- package/src/native/Recordings/App/ContentView.swift +83 -0
- package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
- package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
- package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
- package/src/native/Recordings/App/RecordingsApp.swift +367 -44
- package/src/native/Recordings/App/RecordingsListView.swift +130 -0
- package/src/native/Recordings/App/RecordingsStore.swift +206 -0
- package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
- package/src/native/Recordings/App/SidebarView.swift +212 -0
- package/src/native/Recordings/App/Theme.swift +87 -0
- package/src/native/Recordings/Package.resolved +5 -5
- package/src/native/Recordings/Package.swift +62 -2
- package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
- package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
- package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
- package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
- package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
- package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
- package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
- package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
- package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
- package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
- package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
- package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
- package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
- package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
- package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
- package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
- package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
- package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
- package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
- package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
- package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
- package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
- package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
- package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
- package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
- package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
- package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
- package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
- package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
- package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
- package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
- package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
- package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
- package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
- package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
- package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
- package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
- package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
- package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
- package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
- package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
- package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
- package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
- package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
- package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
- package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
- package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
- package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
- package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
- package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
- package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
- package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
- package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
- package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
- package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
- package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
- package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
- package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
- package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
- package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
- package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
- package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
- package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
- package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
- package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
- package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
- package/src/native/Recordings/build.sh +1973 -236
- package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
umask 077
|
|
4
|
+
|
|
5
|
+
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
6
|
+
export LC_ALL=C
|
|
7
|
+
export LANG=C
|
|
8
|
+
export TZ=UTC0
|
|
9
|
+
|
|
10
|
+
PACKAGE_BOOTSTRAP_ID="__RECORDINGS_PACKAGE_BOOTSTRAP_ID__"
|
|
11
|
+
AUTHORIZATION_ROOT="/private/var/db/com.hasna.recordings.bootstrap-authorization"
|
|
12
|
+
AUTHORIZATION_JOURNAL="${AUTHORIZATION_ROOT}/journal"
|
|
13
|
+
AUTHORIZATION_NEXT="${AUTHORIZATION_ROOT}/.journal.next"
|
|
14
|
+
STATE="/var/db/com.hasna.recordings.updater/release-state.json"
|
|
15
|
+
VERIFIER_USER="_recordingsverify"
|
|
16
|
+
VERIFIER_GROUP="_recordingsverify"
|
|
17
|
+
|
|
18
|
+
[ "$(/usr/bin/id -u)" = "0" ] || {
|
|
19
|
+
echo "Recordings updater bootstrap must run as root through Installer or MDM." >&2
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
path_exists_or_symlink() {
|
|
24
|
+
[ -e "$1" ] || [ -L "$1" ]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
require_no_extended_acl() {
|
|
28
|
+
local path="$1"
|
|
29
|
+
local acl_listing acl_status
|
|
30
|
+
acl_listing="$(/bin/ls -lade "$path")" || {
|
|
31
|
+
echo "Recordings updater could not inspect target ACLs: $path" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
}
|
|
34
|
+
if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
|
|
35
|
+
NR == 1 && length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
|
|
36
|
+
NR > 1 && $1 ~ /^[0-9]+:$/ { found = 1 }
|
|
37
|
+
END { exit found ? 0 : 1 }
|
|
38
|
+
'; then
|
|
39
|
+
echo "Recordings updater target trust path has an extended ACL: $path" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
else
|
|
42
|
+
acl_status=$?
|
|
43
|
+
[ "$acl_status" -eq 1 ] || {
|
|
44
|
+
echo "Recordings updater could not evaluate target ACLs: $path" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
}
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
require_tree_without_extended_acl() {
|
|
51
|
+
local tree="$1"
|
|
52
|
+
local acl_listing acl_status
|
|
53
|
+
require_no_extended_acl "$tree"
|
|
54
|
+
[ -d "$tree" ] || return 0
|
|
55
|
+
acl_listing="$(/bin/ls -laeR "$tree")" || {
|
|
56
|
+
echo "Recordings updater could not recursively inspect target ACLs: $tree" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
}
|
|
59
|
+
if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
|
|
60
|
+
length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
|
|
61
|
+
$1 ~ /^[0-9]+:$/ { found = 1 }
|
|
62
|
+
END { exit found ? 0 : 1 }
|
|
63
|
+
'; then
|
|
64
|
+
echo "Recordings updater target tree has an extended ACL: $tree" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
else
|
|
67
|
+
acl_status=$?
|
|
68
|
+
[ "$acl_status" -eq 1 ] || {
|
|
69
|
+
echo "Recordings updater could not evaluate recursive target ACLs: $tree" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
fi
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
require_root_owned_nonwritable_directory() {
|
|
76
|
+
local directory="$1"
|
|
77
|
+
local mode
|
|
78
|
+
[ -d "$directory" ] && [ ! -L "$directory" ] && \
|
|
79
|
+
[ "$(/usr/bin/stat -f '%u' "$directory")" = 0 ] || {
|
|
80
|
+
echo "Recordings updater target ancestry is missing, linked, or not root-owned: $directory" >&2
|
|
81
|
+
exit 1
|
|
82
|
+
}
|
|
83
|
+
mode="$(/usr/bin/stat -f '%Lp' "$directory")"
|
|
84
|
+
[ $((8#$mode & 8#022)) -eq 0 ] || {
|
|
85
|
+
echo "Recordings updater target ancestry is group/other writable: $directory" >&2
|
|
86
|
+
exit 1
|
|
87
|
+
}
|
|
88
|
+
require_no_extended_acl "$directory"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
require_managed_applications_directory() {
|
|
92
|
+
local mode group_id
|
|
93
|
+
[ -d "/Applications" ] && [ ! -L "/Applications" ] && \
|
|
94
|
+
[ "$(/usr/bin/stat -f '%u' "/Applications")" = 0 ] || {
|
|
95
|
+
echo "Recordings updater requires a root-owned canonical /Applications directory." >&2
|
|
96
|
+
exit 1
|
|
97
|
+
}
|
|
98
|
+
require_no_extended_acl "/Applications"
|
|
99
|
+
mode="$(/usr/bin/stat -f '%Lp' "/Applications")"
|
|
100
|
+
group_id="$(/usr/bin/stat -f '%g' "/Applications")"
|
|
101
|
+
case "$mode:$group_id" in
|
|
102
|
+
755:*|775:80) ;;
|
|
103
|
+
*) echo "Recordings updater requires ACL-free /Applications mode 0755 or root:admin mode 0775." >&2; exit 1 ;;
|
|
104
|
+
esac
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
require_expected_var_link() {
|
|
108
|
+
local var_target
|
|
109
|
+
[ -L "/var" ] || { echo "Recordings updater requires the canonical macOS /var system link." >&2; exit 1; }
|
|
110
|
+
var_target="$(/usr/bin/readlink "/var")" || { echo "Recordings updater could not inspect the macOS /var system link." >&2; exit 1; }
|
|
111
|
+
[ "$var_target" = "private/var" ] && \
|
|
112
|
+
[ "$(cd "/var" && /bin/pwd -P)" = "/private/var" ] || {
|
|
113
|
+
echo "Recordings updater found a noncanonical macOS /var system link." >&2
|
|
114
|
+
exit 1
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
read_journal_value() {
|
|
119
|
+
local key="$1"
|
|
120
|
+
/usr/bin/awk -F= -v key="$key" '
|
|
121
|
+
$1 == key { count += 1; value = substr($0, length(key) + 2) }
|
|
122
|
+
END { if (count != 1) exit 1; print value }
|
|
123
|
+
' "$AUTHORIZATION_JOURNAL"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
require_authorization_journal() {
|
|
127
|
+
path_exists_or_symlink "$AUTHORIZATION_ROOT" && \
|
|
128
|
+
[ -d "$AUTHORIZATION_ROOT" ] && [ ! -L "$AUTHORIZATION_ROOT" ] && \
|
|
129
|
+
[ "$(/usr/bin/stat -f '%u:%g:%Lp' "$AUTHORIZATION_ROOT")" = "0:0:700" ] || {
|
|
130
|
+
echo "Recordings updater authorization evidence is missing or unsafe." >&2
|
|
131
|
+
exit 1
|
|
132
|
+
}
|
|
133
|
+
[ -f "$AUTHORIZATION_JOURNAL" ] && [ ! -L "$AUTHORIZATION_JOURNAL" ] && \
|
|
134
|
+
[ "$(/usr/bin/stat -f '%u:%g:%Lp' "$AUTHORIZATION_JOURNAL")" = "0:0:400" ] && \
|
|
135
|
+
[ "$(/usr/bin/stat -f '%l' "$AUTHORIZATION_JOURNAL")" = "1" ] && \
|
|
136
|
+
[ "$(/usr/bin/wc -l <"$AUTHORIZATION_JOURNAL" | /usr/bin/tr -d ' ')" = "6" ] || {
|
|
137
|
+
echo "Recordings updater authorization journal is missing or unsafe." >&2
|
|
138
|
+
exit 1
|
|
139
|
+
}
|
|
140
|
+
require_no_extended_acl "$AUTHORIZATION_ROOT"
|
|
141
|
+
require_no_extended_acl "$AUTHORIZATION_JOURNAL"
|
|
142
|
+
[ "$(read_journal_value schema_version)" = "1" ] || { echo "Recordings updater authorization journal schema is invalid." >&2; exit 1; }
|
|
143
|
+
JOURNAL_PHASE="$(read_journal_value phase)"
|
|
144
|
+
JOURNAL_PACKAGE_SHA256="$(read_journal_value package_sha256)"
|
|
145
|
+
JOURNAL_PACKAGE_BOOTSTRAP_ID="$(read_journal_value package_bootstrap_id)"
|
|
146
|
+
JOURNAL_INSTALLER_TEAM_ID="$(read_journal_value installer_team_id)"
|
|
147
|
+
JOURNAL_INSTALLER_CERTIFICATE_SHA256="$(read_journal_value installer_certificate_sha256)"
|
|
148
|
+
[[ "$JOURNAL_PACKAGE_SHA256" =~ ^[0-9a-f]{64}$ ]] && \
|
|
149
|
+
[[ "$JOURNAL_PACKAGE_BOOTSTRAP_ID" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]] && \
|
|
150
|
+
[[ "$JOURNAL_INSTALLER_TEAM_ID" =~ ^[A-Z0-9]{10}$ ]] && \
|
|
151
|
+
[[ "$JOURNAL_INSTALLER_CERTIFICATE_SHA256" =~ ^[0-9a-f]{64}$ ]] || {
|
|
152
|
+
echo "Recordings updater authorization journal values are malformed." >&2
|
|
153
|
+
exit 1
|
|
154
|
+
}
|
|
155
|
+
[ "$JOURNAL_PACKAGE_BOOTSTRAP_ID" = "$PACKAGE_BOOTSTRAP_ID" ] || {
|
|
156
|
+
echo "Recordings updater authorization journal belongs to a different authenticated package." >&2
|
|
157
|
+
exit 1
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
write_authorization_phase() {
|
|
162
|
+
local phase="$1"
|
|
163
|
+
local replacement
|
|
164
|
+
replacement="$AUTHORIZATION_NEXT"
|
|
165
|
+
if path_exists_or_symlink "$replacement"; then
|
|
166
|
+
[ -f "$replacement" ] && [ ! -L "$replacement" ] && \
|
|
167
|
+
[ "$(/usr/bin/stat -f '%u:%g' "$replacement")" = "0:0" ] && \
|
|
168
|
+
[ "$(/usr/bin/stat -f '%l' "$replacement")" = "1" ] || {
|
|
169
|
+
echo "Recordings updater authorization journal staging is unsafe." >&2
|
|
170
|
+
exit 1
|
|
171
|
+
}
|
|
172
|
+
case "$(/usr/bin/stat -f '%Lp' "$replacement")" in 400|600) ;; *) echo "Recordings updater authorization journal staging mode is unsafe." >&2; exit 1 ;; esac
|
|
173
|
+
require_no_extended_acl "$replacement"
|
|
174
|
+
/bin/rm -f "$replacement"
|
|
175
|
+
fi
|
|
176
|
+
/usr/bin/printf 'schema_version=1\nphase=%s\npackage_sha256=%s\npackage_bootstrap_id=%s\ninstaller_team_id=%s\ninstaller_certificate_sha256=%s\n' \
|
|
177
|
+
"$phase" "$JOURNAL_PACKAGE_SHA256" "$JOURNAL_PACKAGE_BOOTSTRAP_ID" \
|
|
178
|
+
"$JOURNAL_INSTALLER_TEAM_ID" "$JOURNAL_INSTALLER_CERTIFICATE_SHA256" >"$replacement"
|
|
179
|
+
/usr/sbin/chown root:wheel "$replacement"
|
|
180
|
+
/bin/chmod -N "$replacement"
|
|
181
|
+
/bin/chmod 0400 "$replacement"
|
|
182
|
+
require_no_extended_acl "$replacement"
|
|
183
|
+
/bin/mv -f "$replacement" "$AUTHORIZATION_JOURNAL"
|
|
184
|
+
/bin/sync
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
require_safe_existing_cohort_path() {
|
|
188
|
+
local path="$1"
|
|
189
|
+
local expected_mode="$2"
|
|
190
|
+
local entry listing device mode owner links invalid=0
|
|
191
|
+
[ ! -L "$path" ] && [ "$(/usr/bin/stat -f '%u' "$path")" = "0" ] || {
|
|
192
|
+
echo "Recordings updater authorized exact-package repair found linked or non-root-owned cohort evidence: $path" >&2
|
|
193
|
+
exit 1
|
|
194
|
+
}
|
|
195
|
+
mode="$(/usr/bin/stat -f '%Lp' "$path")"
|
|
196
|
+
case "$expected_mode:$mode" in
|
|
197
|
+
"$mode:$mode"|700-or-755:700|700-or-755:755) ;;
|
|
198
|
+
*)
|
|
199
|
+
echo "Recordings updater authorized exact-package repair found cohort mode drift: $path" >&2
|
|
200
|
+
exit 1
|
|
201
|
+
;;
|
|
202
|
+
esac
|
|
203
|
+
require_tree_without_extended_acl "$path"
|
|
204
|
+
if [ -f "$path" ]; then
|
|
205
|
+
[ "$(/usr/bin/stat -f '%l' "$path")" = "1" ] || { echo "Recordings updater cohort evidence is hard-linked: $path" >&2; exit 1; }
|
|
206
|
+
return
|
|
207
|
+
fi
|
|
208
|
+
[ -d "$path" ] || { echo "Recordings updater cohort evidence is not a regular file or directory: $path" >&2; exit 1; }
|
|
209
|
+
listing="$(/usr/bin/mktemp -t recordings-preinstall-cohort)"
|
|
210
|
+
device="$(/usr/bin/stat -f '%d' "$path")"
|
|
211
|
+
/usr/bin/find -x "$path" -print0 >"$listing" || { /bin/rm -f "$listing"; exit 1; }
|
|
212
|
+
while IFS= read -r -d '' entry; do
|
|
213
|
+
owner="$(/usr/bin/stat -f '%u' "$entry")" || { invalid=1; break; }
|
|
214
|
+
mode="$(/usr/bin/stat -f '%Lp' "$entry")" || { invalid=1; break; }
|
|
215
|
+
[ ! -L "$entry" ] && [ "$owner" = "0" ] && \
|
|
216
|
+
[ "$(/usr/bin/stat -f '%d' "$entry")" = "$device" ] && \
|
|
217
|
+
[ $((8#$mode & 8#022)) -eq 0 ] || { invalid=1; break; }
|
|
218
|
+
if [ -f "$entry" ]; then
|
|
219
|
+
links="$(/usr/bin/stat -f '%l' "$entry")" || { invalid=1; break; }
|
|
220
|
+
[ "$links" = "1" ] || { invalid=1; break; }
|
|
221
|
+
elif [ ! -d "$entry" ]; then
|
|
222
|
+
invalid=1
|
|
223
|
+
break
|
|
224
|
+
fi
|
|
225
|
+
done <"$listing"
|
|
226
|
+
/bin/rm -f "$listing"
|
|
227
|
+
[ "$invalid" -eq 0 ] || { echo "Recordings updater authorized exact-package repair found unsafe cohort structure: $path" >&2; exit 1; }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
require_expected_var_link
|
|
231
|
+
require_managed_applications_directory
|
|
232
|
+
for required_ancestor in \
|
|
233
|
+
"/" "/Library" "/Library/Application Support" "/Library/PrivilegedHelperTools" "/Library/LaunchDaemons" \
|
|
234
|
+
"/private" "/private/var" "/private/var/db"; do
|
|
235
|
+
require_root_owned_nonwritable_directory "$required_ancestor"
|
|
236
|
+
done
|
|
237
|
+
for optional_ancestor in "/Library/Application Support/Hasna" "/Library/Application Support/Hasna/Recordings"; do
|
|
238
|
+
if path_exists_or_symlink "$optional_ancestor"; then require_root_owned_nonwritable_directory "$optional_ancestor"; fi
|
|
239
|
+
done
|
|
240
|
+
|
|
241
|
+
require_authorization_journal
|
|
242
|
+
path_exists_or_symlink "$STATE" && {
|
|
243
|
+
echo "Recordings uses an immutable updater cohort; a bootstrap PKG cannot replace committed anti-rollback state." >&2
|
|
244
|
+
exit 1
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
COHORT_PATHS=(
|
|
248
|
+
"/Library/PrivilegedHelperTools/com.hasna.recordings.updater:555"
|
|
249
|
+
"/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier:555"
|
|
250
|
+
"/Library/LaunchDaemons/com.hasna.recordings.updater.plist:444"
|
|
251
|
+
"/Applications/Recordings.app:755"
|
|
252
|
+
"/Applications/Recordings.app/Contents/Helpers/recordings-update-client:755"
|
|
253
|
+
"/Library/Application Support/Hasna/Recordings/Trust:700-or-755"
|
|
254
|
+
"/Library/Application Support/Hasna/Recordings/Bootstrap:700"
|
|
255
|
+
"/Library/Application Support/Hasna/Recordings/Updates:700"
|
|
256
|
+
"/var/db/com.hasna.recordings.updater:700"
|
|
257
|
+
)
|
|
258
|
+
present_count=0
|
|
259
|
+
for cohort_spec in "${COHORT_PATHS[@]}"; do
|
|
260
|
+
cohort_path="${cohort_spec%:*}"
|
|
261
|
+
cohort_mode="${cohort_spec##*:}"
|
|
262
|
+
if path_exists_or_symlink "$cohort_path"; then
|
|
263
|
+
present_count=$((present_count + 1))
|
|
264
|
+
require_safe_existing_cohort_path "$cohort_path" "$cohort_mode"
|
|
265
|
+
fi
|
|
266
|
+
done
|
|
267
|
+
service_present=false
|
|
268
|
+
if /bin/launchctl print system/com.hasna.recordings.updater >/dev/null 2>&1; then service_present=true; fi
|
|
269
|
+
|
|
270
|
+
case "$JOURNAL_PHASE" in
|
|
271
|
+
authorized)
|
|
272
|
+
JOURNAL_PHASE="authorized"
|
|
273
|
+
[ "$present_count" -eq 0 ] && [ "$service_present" = false ] && \
|
|
274
|
+
! /usr/bin/dscl . -read "/Groups/${VERIFIER_GROUP}" >/dev/null 2>&1 && \
|
|
275
|
+
! /usr/bin/dscl . -read "/Users/${VERIFIER_USER}" >/dev/null 2>&1 || {
|
|
276
|
+
echo "Recordings updater found a partial updater cohort without exact-package authorization or another existing unrelated cohort." >&2
|
|
277
|
+
exit 1
|
|
278
|
+
}
|
|
279
|
+
write_authorization_phase "installer-started"
|
|
280
|
+
;;
|
|
281
|
+
installer-started)
|
|
282
|
+
JOURNAL_PHASE="installer-started"
|
|
283
|
+
[ "$service_present" = false ] || {
|
|
284
|
+
echo "Recordings updater refuses Installer replay while the authorized service is already registered." >&2
|
|
285
|
+
exit 1
|
|
286
|
+
}
|
|
287
|
+
;;
|
|
288
|
+
*) echo "Recordings updater authorization journal has an invalid phase." >&2; exit 1 ;;
|
|
289
|
+
esac
|
|
290
|
+
|
|
291
|
+
# This package remains an immutable, one-time bootstrap cohort. Only an
|
|
292
|
+
# interrupted install bearing this exact signed-script identity may repair its
|
|
293
|
+
# own partial updater cohort; all other replacement requires managed reprovisioning.
|
|
294
|
+
if [ "$present_count" -gt 0 ]; then
|
|
295
|
+
echo "Recordings updater continuing authorized exact-package repair of a partial updater cohort." >&2
|
|
296
|
+
fi
|
|
297
|
+
# Once installed, only app-artifact updates are supported.
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
|
|
5
|
+
echo "Usage: $0 <output-path> <explicit absolute Bun executable path> [native|universal]" >&2
|
|
6
|
+
exit 2
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
BUILD_KIND="${3:-native}"
|
|
10
|
+
case "$BUILD_KIND" in
|
|
11
|
+
native|universal) ;;
|
|
12
|
+
*)
|
|
13
|
+
echo "Companion CLI build kind must be native or universal." >&2
|
|
14
|
+
exit 2
|
|
15
|
+
;;
|
|
16
|
+
esac
|
|
17
|
+
|
|
18
|
+
HOST_UNAME_EXECUTABLE="/usr/bin/uname"
|
|
19
|
+
SYSTEM_DIRNAME_EXECUTABLE="/usr/bin/dirname"
|
|
20
|
+
SYSTEM_PWD_EXECUTABLE="/bin/pwd"
|
|
21
|
+
SANITIZED_PATH="/usr/bin:/bin:/usr/sbin:/sbin"
|
|
22
|
+
|
|
23
|
+
require_executable() {
|
|
24
|
+
local label="$1"
|
|
25
|
+
local executable="$2"
|
|
26
|
+
case "$executable" in
|
|
27
|
+
/*) ;;
|
|
28
|
+
*)
|
|
29
|
+
echo "$label must be configured as an absolute executable path." >&2
|
|
30
|
+
exit 1
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
if [ ! -f "$executable" ] || [ ! -x "$executable" ]; then
|
|
34
|
+
echo "$label is missing or is not an executable file: $executable" >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
require_executable "HOST_UNAME_EXECUTABLE" "$HOST_UNAME_EXECUTABLE"
|
|
40
|
+
require_executable "SYSTEM_DIRNAME_EXECUTABLE" "$SYSTEM_DIRNAME_EXECUTABLE"
|
|
41
|
+
require_executable "SYSTEM_PWD_EXECUTABLE" "$SYSTEM_PWD_EXECUTABLE"
|
|
42
|
+
HOST_PLATFORM="$("$HOST_UNAME_EXECUTABLE" -s)"
|
|
43
|
+
readonly HOST_PLATFORM
|
|
44
|
+
|
|
45
|
+
select_executable() {
|
|
46
|
+
local system_executable="$1"
|
|
47
|
+
local test_override="${2:-}"
|
|
48
|
+
if [ "$HOST_PLATFORM" = "Darwin" ] || [ -z "$test_override" ]; then
|
|
49
|
+
printf '%s\n' "$system_executable"
|
|
50
|
+
else
|
|
51
|
+
printf '%s\n' "$test_override"
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
DIRNAME_EXECUTABLE="$(select_executable "$SYSTEM_DIRNAME_EXECUTABLE" "${RECORDINGS_TEST_COMPANION_DIRNAME_EXECUTABLE:-}")"
|
|
56
|
+
PWD_EXECUTABLE="$(select_executable "$SYSTEM_PWD_EXECUTABLE" "${RECORDINGS_TEST_COMPANION_PWD_EXECUTABLE:-}")"
|
|
57
|
+
CHMOD_EXECUTABLE="$(select_executable "/bin/chmod" "${RECORDINGS_TEST_COMPANION_CHMOD_EXECUTABLE:-}")"
|
|
58
|
+
CP_EXECUTABLE="$(select_executable "/bin/cp" "${RECORDINGS_TEST_COMPANION_CP_EXECUTABLE:-}")"
|
|
59
|
+
ENV_EXECUTABLE="$(select_executable "/usr/bin/env" "${RECORDINGS_TEST_COMPANION_ENV_EXECUTABLE:-}")"
|
|
60
|
+
GREP_EXECUTABLE="$(select_executable "/usr/bin/grep" "${RECORDINGS_TEST_COMPANION_GREP_EXECUTABLE:-}")"
|
|
61
|
+
MKDIR_EXECUTABLE="$(select_executable "/bin/mkdir" "${RECORDINGS_TEST_COMPANION_MKDIR_EXECUTABLE:-}")"
|
|
62
|
+
MKTEMP_EXECUTABLE="$(select_executable "/usr/bin/mktemp" "${RECORDINGS_TEST_COMPANION_MKTEMP_EXECUTABLE:-}")"
|
|
63
|
+
MV_EXECUTABLE="$(select_executable "/bin/mv" "${RECORDINGS_TEST_COMPANION_MV_EXECUTABLE:-}")"
|
|
64
|
+
RM_EXECUTABLE="$(select_executable "/bin/rm" "${RECORDINGS_TEST_COMPANION_RM_EXECUTABLE:-}")"
|
|
65
|
+
LIPO_EXECUTABLE="$(select_executable "/usr/bin/lipo" "${RECORDINGS_TEST_COMPANION_LIPO_EXECUTABLE:-}")"
|
|
66
|
+
|
|
67
|
+
for executable_spec in \
|
|
68
|
+
"DIRNAME_EXECUTABLE:$DIRNAME_EXECUTABLE" \
|
|
69
|
+
"PWD_EXECUTABLE:$PWD_EXECUTABLE" \
|
|
70
|
+
"CHMOD_EXECUTABLE:$CHMOD_EXECUTABLE" \
|
|
71
|
+
"CP_EXECUTABLE:$CP_EXECUTABLE" \
|
|
72
|
+
"ENV_EXECUTABLE:$ENV_EXECUTABLE" \
|
|
73
|
+
"GREP_EXECUTABLE:$GREP_EXECUTABLE" \
|
|
74
|
+
"MKDIR_EXECUTABLE:$MKDIR_EXECUTABLE" \
|
|
75
|
+
"MKTEMP_EXECUTABLE:$MKTEMP_EXECUTABLE" \
|
|
76
|
+
"MV_EXECUTABLE:$MV_EXECUTABLE" \
|
|
77
|
+
"RM_EXECUTABLE:$RM_EXECUTABLE"; do
|
|
78
|
+
require_executable "${executable_spec%%:*}" "${executable_spec#*:}"
|
|
79
|
+
done
|
|
80
|
+
if [ "$BUILD_KIND" = "universal" ]; then
|
|
81
|
+
require_executable "LIPO_EXECUTABLE" "$LIPO_EXECUTABLE"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
BUN_EXECUTABLE="$2"
|
|
85
|
+
require_bun_executable() {
|
|
86
|
+
local executable="$1"
|
|
87
|
+
require_executable "BUN_EXECUTABLE" "$executable"
|
|
88
|
+
if ! "$ENV_EXECUTABLE" -i \
|
|
89
|
+
HOME="/tmp" \
|
|
90
|
+
PATH="$SANITIZED_PATH" \
|
|
91
|
+
"$executable" -e '
|
|
92
|
+
import { realpathSync, statSync } from "node:fs";
|
|
93
|
+
const expected = process.argv[1];
|
|
94
|
+
const actual = process.execPath;
|
|
95
|
+
if (!expected || realpathSync(actual) !== realpathSync(expected)) process.exit(66);
|
|
96
|
+
if (!statSync(actual).isFile()) process.exit(66);
|
|
97
|
+
if (!/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(Bun.version)) process.exit(66);
|
|
98
|
+
' "$executable"; then
|
|
99
|
+
echo "BUN_EXECUTABLE did not identify the Bun executable that actually ran." >&2
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
require_bun_executable "$BUN_EXECUTABLE"
|
|
104
|
+
|
|
105
|
+
ROOT="$(cd "$("$DIRNAME_EXECUTABLE" "${BASH_SOURCE[0]}")/.." && "$PWD_EXECUTABLE" -P)"
|
|
106
|
+
CURRENT_DIRECTORY="$($PWD_EXECUTABLE -P)"
|
|
107
|
+
OUTPUT="$1"
|
|
108
|
+
case "$OUTPUT" in
|
|
109
|
+
/*) ;;
|
|
110
|
+
*) OUTPUT="${CURRENT_DIRECTORY}/${OUTPUT}" ;;
|
|
111
|
+
esac
|
|
112
|
+
case "$OUTPUT" in
|
|
113
|
+
*/)
|
|
114
|
+
echo "Companion CLI output must be a file path, not a directory path: ${OUTPUT}." >&2
|
|
115
|
+
exit 1
|
|
116
|
+
;;
|
|
117
|
+
esac
|
|
118
|
+
if [ -d "$OUTPUT" ]; then
|
|
119
|
+
echo "Companion CLI output must not be an existing directory: ${OUTPUT}." >&2
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
ENTRY="${ROOT}/src/cli/index.ts"
|
|
124
|
+
if [ ! -f "$ENTRY" ]; then
|
|
125
|
+
echo "Recordings CLI source is missing from ${ROOT}." >&2
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
for required_source in package.json bun.lock bunfig.toml tsconfig.json; do
|
|
129
|
+
if [ ! -f "${ROOT}/${required_source}" ]; then
|
|
130
|
+
echo "Recordings companion build input is missing: ${ROOT}/${required_source}." >&2
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
WORK_DIR="$($MKTEMP_EXECUTABLE -d /tmp/recordings-companion-build.XXXXXX)"
|
|
136
|
+
PUBLISH_OUTPUT=""
|
|
137
|
+
cleanup() {
|
|
138
|
+
if [ -n "$PUBLISH_OUTPUT" ]; then
|
|
139
|
+
"$RM_EXECUTABLE" -f "$PUBLISH_OUTPUT"
|
|
140
|
+
fi
|
|
141
|
+
"$RM_EXECUTABLE" -rf "$WORK_DIR"
|
|
142
|
+
}
|
|
143
|
+
trap cleanup EXIT
|
|
144
|
+
|
|
145
|
+
BUILD_HOME="$WORK_DIR/home"
|
|
146
|
+
BUN_CACHE="$WORK_DIR/bun-cache"
|
|
147
|
+
STAGED_ROOT="$WORK_DIR/package"
|
|
148
|
+
COMPILE_DIR="$WORK_DIR/compile"
|
|
149
|
+
COMPILED_OUTPUT="$COMPILE_DIR/recordings"
|
|
150
|
+
COMPILED_ARM64="$COMPILE_DIR/recordings.arm64"
|
|
151
|
+
COMPILED_X86_64="$COMPILE_DIR/recordings.x86_64"
|
|
152
|
+
"$MKDIR_EXECUTABLE" -p "$BUILD_HOME" "$BUN_CACHE" "$STAGED_ROOT" "$COMPILE_DIR"
|
|
153
|
+
"$CP_EXECUTABLE" "$ROOT/package.json" "$ROOT/bun.lock" "$ROOT/bunfig.toml" "$ROOT/tsconfig.json" "$STAGED_ROOT/"
|
|
154
|
+
"$CP_EXECUTABLE" -R "$ROOT/src" "$STAGED_ROOT/src"
|
|
155
|
+
if [ -d "$ROOT/migrations" ]; then
|
|
156
|
+
"$CP_EXECUTABLE" -R "$ROOT/migrations" "$STAGED_ROOT/migrations"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
run_bun() {
|
|
160
|
+
"$ENV_EXECUTABLE" -i \
|
|
161
|
+
HOME="$BUILD_HOME" \
|
|
162
|
+
PATH="$SANITIZED_PATH" \
|
|
163
|
+
TMPDIR="$WORK_DIR" \
|
|
164
|
+
"$BUN_EXECUTABLE" "$@"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
run_lipo() {
|
|
168
|
+
"$ENV_EXECUTABLE" -i \
|
|
169
|
+
HOME="$BUILD_HOME" \
|
|
170
|
+
PATH="$SANITIZED_PATH" \
|
|
171
|
+
TMPDIR="$WORK_DIR" \
|
|
172
|
+
"$LIPO_EXECUTABLE" "$@"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
verify_exact_binary_architectures() {
|
|
176
|
+
local binary="$1"
|
|
177
|
+
shift
|
|
178
|
+
local actual_architectures
|
|
179
|
+
local actual_architecture
|
|
180
|
+
local expected_architecture
|
|
181
|
+
local actual_count=0
|
|
182
|
+
local match_count
|
|
183
|
+
|
|
184
|
+
if ! actual_architectures="$(run_lipo -archs "$binary")"; then
|
|
185
|
+
echo "Could not read companion CLI architectures: $binary" >&2
|
|
186
|
+
return 1
|
|
187
|
+
fi
|
|
188
|
+
for actual_architecture in $actual_architectures; do
|
|
189
|
+
actual_count=$((actual_count + 1))
|
|
190
|
+
case " $* " in
|
|
191
|
+
*" $actual_architecture "*) ;;
|
|
192
|
+
*)
|
|
193
|
+
echo "Companion CLI contains unsupported architecture: $actual_architecture" >&2
|
|
194
|
+
return 1
|
|
195
|
+
;;
|
|
196
|
+
esac
|
|
197
|
+
done
|
|
198
|
+
if [ "$actual_count" -ne "$#" ]; then
|
|
199
|
+
echo "Companion CLI architecture count does not match the universal release policy." >&2
|
|
200
|
+
return 1
|
|
201
|
+
fi
|
|
202
|
+
for expected_architecture in "$@"; do
|
|
203
|
+
match_count=0
|
|
204
|
+
for actual_architecture in $actual_architectures; do
|
|
205
|
+
if [ "$actual_architecture" = "$expected_architecture" ]; then
|
|
206
|
+
match_count=$((match_count + 1))
|
|
207
|
+
fi
|
|
208
|
+
done
|
|
209
|
+
if [ "$match_count" -ne 1 ]; then
|
|
210
|
+
echo "Companion CLI must contain exactly one $expected_architecture slice." >&2
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
run_bun install \
|
|
217
|
+
--cwd "$STAGED_ROOT" \
|
|
218
|
+
--production \
|
|
219
|
+
--frozen-lockfile \
|
|
220
|
+
--ignore-scripts \
|
|
221
|
+
--minimum-release-age=604800 \
|
|
222
|
+
--cache-dir "$BUN_CACHE" \
|
|
223
|
+
--no-progress
|
|
224
|
+
|
|
225
|
+
if [ "$BUILD_KIND" = "universal" ]; then
|
|
226
|
+
(
|
|
227
|
+
cd "$COMPILE_DIR"
|
|
228
|
+
run_bun build \
|
|
229
|
+
--compile \
|
|
230
|
+
--target="bun-darwin-arm64" \
|
|
231
|
+
--reject-unresolved \
|
|
232
|
+
--no-compile-autoload-dotenv \
|
|
233
|
+
--no-compile-autoload-bunfig \
|
|
234
|
+
--no-compile-autoload-tsconfig \
|
|
235
|
+
--no-compile-autoload-package-json \
|
|
236
|
+
"$STAGED_ROOT/src/cli/index.ts" \
|
|
237
|
+
--outfile "$COMPILED_ARM64"
|
|
238
|
+
run_bun build \
|
|
239
|
+
--compile \
|
|
240
|
+
--target="bun-darwin-x64" \
|
|
241
|
+
--reject-unresolved \
|
|
242
|
+
--no-compile-autoload-dotenv \
|
|
243
|
+
--no-compile-autoload-bunfig \
|
|
244
|
+
--no-compile-autoload-tsconfig \
|
|
245
|
+
--no-compile-autoload-package-json \
|
|
246
|
+
"$STAGED_ROOT/src/cli/index.ts" \
|
|
247
|
+
--outfile "$COMPILED_X86_64"
|
|
248
|
+
)
|
|
249
|
+
run_lipo -create "$COMPILED_ARM64" "$COMPILED_X86_64" -output "$COMPILED_OUTPUT"
|
|
250
|
+
verify_exact_binary_architectures "$COMPILED_OUTPUT" arm64 x86_64
|
|
251
|
+
else
|
|
252
|
+
(
|
|
253
|
+
cd "$COMPILE_DIR"
|
|
254
|
+
run_bun build \
|
|
255
|
+
--compile \
|
|
256
|
+
--reject-unresolved \
|
|
257
|
+
--no-compile-autoload-dotenv \
|
|
258
|
+
--no-compile-autoload-bunfig \
|
|
259
|
+
--no-compile-autoload-tsconfig \
|
|
260
|
+
--no-compile-autoload-package-json \
|
|
261
|
+
"$STAGED_ROOT/src/cli/index.ts" \
|
|
262
|
+
--outfile "$COMPILED_OUTPUT"
|
|
263
|
+
)
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
EXPECTED_VERSION="$(
|
|
267
|
+
cd "$STAGED_ROOT"
|
|
268
|
+
run_bun -e 'const pkg = await Bun.file("package.json").json(); process.stdout.write(pkg.version)'
|
|
269
|
+
)"
|
|
270
|
+
run_output() {
|
|
271
|
+
"$ENV_EXECUTABLE" -i \
|
|
272
|
+
HOME="$BUILD_HOME" \
|
|
273
|
+
PATH="$SANITIZED_PATH" \
|
|
274
|
+
TMPDIR="$WORK_DIR" \
|
|
275
|
+
"$COMPILED_OUTPUT" "$@"
|
|
276
|
+
}
|
|
277
|
+
ACTUAL_VERSION="$(run_output --version)"
|
|
278
|
+
|
|
279
|
+
if [ "$ACTUAL_VERSION" != "$EXPECTED_VERSION" ]; then
|
|
280
|
+
echo "Companion CLI version mismatch: expected ${EXPECTED_VERSION}, got ${ACTUAL_VERSION}." >&2
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
require_command() {
|
|
285
|
+
if ! run_output "$@" --help >/dev/null 2>&1; then
|
|
286
|
+
echo "Companion CLI is missing command: $*." >&2
|
|
287
|
+
exit 1
|
|
288
|
+
fi
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
require_flag() {
|
|
292
|
+
local command_name="$1"
|
|
293
|
+
local flag="$2"
|
|
294
|
+
shift 2
|
|
295
|
+
if ! run_output "$@" --help | "$GREP_EXECUTABLE" -Fq -- "$flag"; then
|
|
296
|
+
echo "Companion CLI is missing ${command_name} ${flag}." >&2
|
|
297
|
+
exit 1
|
|
298
|
+
fi
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# Keep this contract in lockstep with every command and option emitted by the Swift app.
|
|
302
|
+
for command in list show search stats delete transcribe save-text rewrite; do
|
|
303
|
+
require_command "$command"
|
|
304
|
+
done
|
|
305
|
+
require_command project register
|
|
306
|
+
|
|
307
|
+
require_flag "root" "--json"
|
|
308
|
+
require_flag "root" "--project"
|
|
309
|
+
for flag in --name --path --description; do
|
|
310
|
+
require_flag "project register" "$flag" project register
|
|
311
|
+
done
|
|
312
|
+
for flag in -n --limit --offset; do
|
|
313
|
+
require_flag "list" "$flag" list
|
|
314
|
+
done
|
|
315
|
+
require_flag "search" "-n" search
|
|
316
|
+
require_flag "search" "--limit" search
|
|
317
|
+
for flag in --post-processing --prompt --transcriber-prompt --language --recording-id --transcription-model --transcriber-model --enhancement-model --enhance-triggers-json --keyword-transforms-json; do
|
|
318
|
+
require_flag "transcribe" "$flag" transcribe
|
|
319
|
+
done
|
|
320
|
+
for flag in --text-file --source --model-used --post-processing --audio-path --duration-ms --language --transcriber-prompt --recording-id --transcription-model --transcriber-model --enhancement-model --enhance-triggers-json --keyword-transforms-json; do
|
|
321
|
+
require_flag "save-text" "$flag" save-text
|
|
322
|
+
done
|
|
323
|
+
for flag in --instruction --post-processing --language --prompt --transcriber-prompt --transcription-model --transcriber-model --enhancement-model --enhance-triggers-json --keyword-transforms-json; do
|
|
324
|
+
require_flag "rewrite" "$flag" rewrite
|
|
325
|
+
done
|
|
326
|
+
|
|
327
|
+
OUTPUT_DIRECTORY="$("$DIRNAME_EXECUTABLE" "$OUTPUT")"
|
|
328
|
+
"$MKDIR_EXECUTABLE" -p "$OUTPUT_DIRECTORY"
|
|
329
|
+
PUBLISH_OUTPUT="$($MKTEMP_EXECUTABLE "$OUTPUT_DIRECTORY/.recordings-companion.XXXXXX")"
|
|
330
|
+
"$MV_EXECUTABLE" "$COMPILED_OUTPUT" "$PUBLISH_OUTPUT"
|
|
331
|
+
"$CHMOD_EXECUTABLE" 0755 "$PUBLISH_OUTPUT"
|
|
332
|
+
run_bun -e '
|
|
333
|
+
import { renameSync } from "node:fs";
|
|
334
|
+
renameSync(process.argv[1], process.argv[2]);
|
|
335
|
+
' "$PUBLISH_OUTPUT" "$OUTPUT"
|
|
336
|
+
PUBLISH_OUTPUT=""
|
|
337
|
+
echo "Built Recordings companion CLI ${ACTUAL_VERSION}: ${OUTPUT}"
|