@hasna/recordings 0.3.2 → 0.3.9

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 (59) hide show
  1. package/README.md +38 -34
  2. package/bun.lock +96 -36
  3. package/dist/__tests__/helpers/source-assertions.d.ts +6 -4
  4. package/dist/__tests__/helpers/source-assertions.d.ts.map +1 -1
  5. package/dist/cli/index.js +1059 -219
  6. package/dist/cli/macos-shortcut.d.ts +2 -2
  7. package/dist/db/pg-migrations.d.ts +1 -1
  8. package/dist/http/client.d.ts +6 -14
  9. package/dist/http/client.d.ts.map +1 -1
  10. package/dist/index.js +1971 -175
  11. package/dist/lib/capture-probe.d.ts +3 -3
  12. package/dist/lib/capture-probe.d.ts.map +1 -1
  13. package/dist/lib/config.d.ts.map +1 -1
  14. package/dist/lib/macos-bundle.d.ts +1 -1
  15. package/dist/lib/release-install-policy.d.ts +21 -0
  16. package/dist/lib/release-install-policy.d.ts.map +1 -1
  17. package/dist/mcp/index.js +978 -142
  18. package/dist/sdk/index.d.ts +2 -2
  19. package/dist/sdk/index.js +7 -3
  20. package/dist/sdk/v1.generated.d.ts.map +1 -1
  21. package/dist/server/cloud-config.d.ts +4 -19
  22. package/dist/server/cloud-config.d.ts.map +1 -1
  23. package/dist/server/cloud.d.ts +1 -1
  24. package/dist/server/cloud.d.ts.map +1 -1
  25. package/dist/server/index.js +217 -299
  26. package/dist/server/serve.d.ts.map +1 -1
  27. package/dist/storage.d.ts +1 -1
  28. package/dist/storage.d.ts.map +1 -1
  29. package/dist/storage.js +1881 -90
  30. package/dist/store.d.ts.map +1 -1
  31. package/dist/version.d.ts +1 -1
  32. package/dist/version.d.ts.map +1 -1
  33. package/package.json +4 -3
  34. package/packaging/macos/build_release_pkg.sh +10 -4
  35. package/packaging/macos/managed_bootstrap.sh +4 -4
  36. package/packaging/macos/scripts/postinstall +2 -2
  37. package/packaging/macos/scripts/preinstall +2 -0
  38. package/scripts/generate-sdk.ts +17 -16
  39. package/scripts/install_macos_app.sh +22 -22
  40. package/scripts/macos_artifact.ts +48 -39
  41. package/scripts/migrate.ts +2 -2
  42. package/scripts/release-suite-gate.ts +174 -0
  43. package/scripts/set-version.ts +5 -5
  44. package/scripts/smoke_macos_app.sh +21 -21
  45. package/scripts/vacuity-manifests/version-sites.tsv +8 -4
  46. package/src/native/Recordings/RecordingsLib/Info.plist +3 -3
  47. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +2 -2
  48. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +1 -1
  49. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +1 -1
  50. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +2 -2
  51. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +1 -1
  52. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +1 -1
  53. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +1 -1
  54. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +2 -2
  55. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +1 -1
  56. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +2 -2
  57. package/src/native/Recordings/build.sh +14 -20
  58. package/dist/lib/retired-deployment-modes.d.ts +0 -24
  59. package/dist/lib/retired-deployment-modes.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,KAAK,EACL,OAAO,EACR,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAqC,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMzF,eAAO,MAAM,GAAG,eAAe,CAAC;AAEhC,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1F,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,eAAe,CAAC,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAChF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAG7C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClD,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/B,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACxD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAGjF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAGnC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAwPD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,KAAK,EACZ,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,KAAK,CAMzG;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACpB,eAAe,EACf,KAAK,EACL,OAAO,EACR,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAqC,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAUzF,eAAO,MAAM,GAAG,eAAe,CAAC;AAEhC,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1F,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACpD,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,eAAe,CAAC,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAChF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAG7C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClD,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/B,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACxD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAGjF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAGnC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AA4QD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,KAAK,EACZ,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,KAAK,CAMzG;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.3.2";
1
+ export declare const VERSION: string;
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,OAAO,QAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/recordings",
3
- "version": "0.3.2",
3
+ "version": "0.3.9",
4
4
  "type": "module",
5
5
  "description": "Speech-to-text recording tool with MCP and CLI — records, transcribes, and optionally enhances text using AI",
6
6
  "repository": {
@@ -63,7 +63,8 @@
63
63
  "version:check": "bun run scripts/set-version.ts --check",
64
64
  "prepack": "bun run prepack:platform-gate && bun run version:check && bun run build && bun run verify:release && bun run scan:artifact",
65
65
  "prepack:platform-gate": "bash -c 'if [ \"$(uname -s)\" = Darwin ]; then bun run build:native-fs-guard; else echo \"WARN: native fs-guard build skipped on $(uname -s) — dry-run/CI pack; publish recordings from macOS\"; fi'",
66
- "prepublishOnly": "bun run typecheck && bun run test",
66
+ "prepublishOnly": "bun run typecheck && bun run release-suite-gate",
67
+ "release-suite-gate": "bun run scripts/release-suite-gate.ts",
67
68
  "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
68
69
  },
69
70
  "files": [
@@ -83,7 +84,7 @@
83
84
  "LICENSE"
84
85
  ],
85
86
  "dependencies": {
86
- "@hasna/contracts": "0.8.4",
87
+ "@hasna/contracts": "0.13.4",
87
88
  "@hasna/events": "0.1.11",
88
89
  "@modelcontextprotocol/sdk": "^1.12.1",
89
90
  "chalk": "^5.4.1",
@@ -222,8 +222,14 @@ done
222
222
  case "$VERSION" in
223
223
  ''|*[!0-9A-Za-z._+-]*) echo "Package version is missing or invalid." >&2; exit 2 ;;
224
224
  esac
225
- [ "$ARTIFACT_BASENAME" = "Recordings-${VERSION}-macos-initial-bootstrap" ] || {
226
- echo "Initial-bootstrap package artifact basename is missing or mode-incompatible." >&2
225
+ # The artifact basename is derived from the canonical bundle identity, never hardcoded:
226
+ # build.sh emits ${APP_BASENAME}-${VERSION}-macos-${RELEASE_SUBTYPE} with APP_BASENAME
227
+ # taken from the bundle name (currently HasnaRecordings.app). The legacy
228
+ # Recordings-* form was dropped with the bundle rename and must not be re-required.
229
+ [ -n "$APP" ] && [ -d "$APP" ] || { echo "Package app bundle path is missing or invalid." >&2; exit 2; }
230
+ expected_basename="$(/usr/bin/basename "$APP" .app)-${VERSION}-macos-initial-bootstrap"
231
+ [ "$ARTIFACT_BASENAME" = "$expected_basename" ] || {
232
+ echo "Initial-bootstrap package artifact basename is missing or mode-incompatible (expected ${expected_basename})." >&2
227
233
  exit 2
228
234
  }
229
235
  [[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]] || { echo "Package source SHA must be an exact commit." >&2; exit 2; }
@@ -527,7 +533,7 @@ PACKAGE_BOOTSTRAP_ID="$(/usr/bin/uuidgen | /usr/bin/tr '[:upper:]' '[:lower:]')"
527
533
  "$SCRIPTS"
528
534
  require_no_extended_acl "$PACKAGE_STAGE_DIR"
529
535
 
530
- /usr/bin/ditto "$APP" "$ROOT/Applications/Recordings.app"
536
+ /usr/bin/ditto "$APP" "$ROOT/Applications/HasnaRecordings.app"
531
537
  /bin/cp "$BROKER" "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
532
538
  /bin/cp "$VERIFIER" "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
533
539
  /bin/cp "$PUBLIC_KEY" "$ROOT/Library/Application Support/Hasna/Recordings/Trust/envelope-keys/${KEY_EPOCH}.raw"
@@ -576,7 +582,7 @@ require_tree_without_extended_acl "$SCRIPTS"
576
582
  "$ROOT/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
577
583
  /usr/bin/plutil -lint "$ROOT/Library/LaunchDaemons/com.hasna.recordings.updater.plist" >/dev/null
578
584
 
579
- STAGED_APP="$ROOT/Applications/Recordings.app"
585
+ STAGED_APP="$ROOT/Applications/HasnaRecordings.app"
580
586
  STAGED_BROKER="$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
581
587
  STAGED_CLIENT="$STAGED_APP/Contents/Helpers/recordings-update-client"
582
588
  STAGED_VERIFIER="$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
@@ -173,7 +173,7 @@ validate_target_ancestry() {
173
173
 
174
174
  validate_target_ancestry
175
175
 
176
- CLIENT="/Applications/Recordings.app/Contents/Helpers/recordings-update-client"
176
+ CLIENT="/Applications/HasnaRecordings.app/Contents/Helpers/recordings-update-client"
177
177
  STATE="/var/db/com.hasna.recordings.updater/release-state.json"
178
178
  AUTHORIZATION_ROOT="/private/var/db/com.hasna.recordings.bootstrap-authorization"
179
179
  AUTHORIZATION_JOURNAL="${AUTHORIZATION_ROOT}/journal"
@@ -182,7 +182,7 @@ COHORT_PATHS=(
182
182
  "/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
183
183
  "/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
184
184
  "/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
185
- "/Applications/Recordings.app"
185
+ "/Applications/HasnaRecordings.app"
186
186
  "$CLIENT"
187
187
  "/Library/Application Support/Hasna/Recordings/Trust"
188
188
  "/Library/Application Support/Hasna/Recordings/Bootstrap"
@@ -304,7 +304,7 @@ PAYLOAD="$EXPANDED/Payload"
304
304
  PREFLIGHT="$EXPANDED/Scripts/recordings-bootstrap-preflight"
305
305
  EXPANDED_PREINSTALL="$EXPANDED/Scripts/preinstall"
306
306
  EXPANDED_POSTINSTALL="$EXPANDED/Scripts/postinstall"
307
- PAYLOAD_CLIENT="$PAYLOAD/Applications/Recordings.app/Contents/Helpers/recordings-update-client"
307
+ PAYLOAD_CLIENT="$PAYLOAD/Applications/HasnaRecordings.app/Contents/Helpers/recordings-update-client"
308
308
  [ -d "$PAYLOAD" ] && [ ! -L "$PAYLOAD" ] && \
309
309
  [ -x "$PREFLIGHT" ] && [ ! -L "$PREFLIGHT" ] && \
310
310
  [ -x "$EXPANDED_PREINSTALL" ] && [ ! -L "$EXPANDED_PREINSTALL" ] && \
@@ -512,7 +512,7 @@ cohort_expected_mode() {
512
512
  "/Library/PrivilegedHelperTools/com.hasna.recordings.updater"|\
513
513
  "/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier") printf '555\n' ;;
514
514
  "/Library/LaunchDaemons/com.hasna.recordings.updater.plist") printf '444\n' ;;
515
- "/Applications/Recordings.app"|"$CLIENT") printf '755\n' ;;
515
+ "/Applications/HasnaRecordings.app"|"$CLIENT") printf '755\n' ;;
516
516
  "/Library/Application Support/Hasna/Recordings/Trust") printf '700-or-755\n' ;;
517
517
  "/Library/Application Support/Hasna/Recordings/Bootstrap"|\
518
518
  "/Library/Application Support/Hasna/Recordings/Updates"|\
@@ -11,9 +11,9 @@ PACKAGE_BOOTSTRAP_ID="__RECORDINGS_PACKAGE_BOOTSTRAP_ID__"
11
11
  AUTHORIZATION_ROOT="/private/var/db/com.hasna.recordings.bootstrap-authorization"
12
12
  AUTHORIZATION_JOURNAL="${AUTHORIZATION_ROOT}/journal"
13
13
  BROKER="/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
14
- CLIENT="/Applications/Recordings.app/Contents/Helpers/recordings-update-client"
14
+ CLIENT="/Applications/HasnaRecordings.app/Contents/Helpers/recordings-update-client"
15
15
  VERIFIER="/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
16
- APP="/Applications/Recordings.app"
16
+ APP="/Applications/HasnaRecordings.app"
17
17
  STATE_ROOT="/Library/Application Support/Hasna/Recordings/Updates"
18
18
  TRUST_ROOT="/Library/Application Support/Hasna/Recordings/Trust"
19
19
  BOOTSTRAP_ROOT="/Library/Application Support/Hasna/Recordings/Bootstrap"
@@ -248,6 +248,8 @@ COHORT_PATHS=(
248
248
  "/Library/PrivilegedHelperTools/com.hasna.recordings.updater:555"
249
249
  "/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier:555"
250
250
  "/Library/LaunchDaemons/com.hasna.recordings.updater.plist:444"
251
+ "/Applications/HasnaRecordings.app:755"
252
+ "/Applications/HasnaRecordings.app/Contents/Helpers/recordings-update-client:755"
251
253
  "/Applications/Recordings.app:755"
252
254
  "/Applications/Recordings.app/Contents/Helpers/recordings-update-client:755"
253
255
  "/Library/Application Support/Hasna/Recordings/Trust:700-or-755"
@@ -20,23 +20,24 @@ const { code, operations, warnings } = generateSdkFromOpenApi(spec as never, {
20
20
  apiKeyHeader: "x-api-key",
21
21
  });
22
22
 
23
- // @hasna/contracts 0.8.4 stringifies query arrays. Preserve OpenAPI's
24
- // form+explode semantics until the package generator ships array support.
25
- const scalarQuerySerialization =
26
- " if (value !== undefined && value !== null) url.searchParams.set(key, String(value));";
27
- if (!code.includes(scalarQuerySerialization)) {
28
- throw new Error("Generated SDK query serializer changed; update the array compatibility transform");
23
+ // @hasna/contracts 0.13.1 emits array-aware query serialization natively
24
+ // per-item append, null/undefined items skipped which is exactly OpenAPI's
25
+ // form+explode semantics the 0.8.4-era transform used to add by hand. Assert
26
+ // the measured 0.13.1 shape so a future serializer change fails generation
27
+ // loudly instead of silently altering request semantics.
28
+ const arrayQuerySerialization =
29
+ " if (value === undefined || value === null) continue;\n" +
30
+ " if (Array.isArray(value)) {\n" +
31
+ " for (const item of value) {\n" +
32
+ " if (item !== undefined && item !== null) url.searchParams.append(key, String(item));\n" +
33
+ " }\n" +
34
+ " } else {\n" +
35
+ " url.searchParams.set(key, String(value));\n" +
36
+ " }";
37
+ if (!code.includes(arrayQuerySerialization)) {
38
+ throw new Error("Generated SDK query serializer changed; update the serializer shape assertion");
29
39
  }
30
- const compatibleCode = code.replace(
31
- scalarQuerySerialization,
32
- [
33
- " if (Array.isArray(value)) {",
34
- " for (const item of value) url.searchParams.append(key, String(item));",
35
- " } else if (value !== undefined && value !== null) {",
36
- " url.searchParams.set(key, String(value));",
37
- " }",
38
- ].join("\n"),
39
- );
40
+ const compatibleCode = code;
40
41
 
41
42
  const header =
42
43
  "// AUTO-GENERATED by scripts/generate-sdk.ts from src/server/openapi.ts — DO NOT EDIT BY HAND.\n" +
@@ -126,18 +126,18 @@ require_absolute_canonical_owned_home() {
126
126
  local canonical
127
127
  case "$candidate" in
128
128
  /*) ;;
129
- *) echo "Recordings.app installation requires HOME to be an absolute canonical path." >&2; exit 1 ;;
129
+ *) echo "HasnaRecordings.app installation requires HOME to be an absolute canonical path." >&2; exit 1 ;;
130
130
  esac
131
131
  [ -d "$candidate" ] && [ ! -L "$candidate" ] || {
132
- echo "Recordings.app installation requires HOME to be an absolute canonical path to a non-symlink directory." >&2
132
+ echo "HasnaRecordings.app installation requires HOME to be an absolute canonical path to a non-symlink directory." >&2
133
133
  exit 1
134
134
  }
135
135
  canonical="$(cd "$candidate" 2>/dev/null && builtin pwd -P)" || {
136
- echo "Recordings.app installation could not resolve HOME canonically." >&2
136
+ echo "HasnaRecordings.app installation could not resolve HOME canonically." >&2
137
137
  exit 1
138
138
  }
139
139
  [ "$candidate" = "$canonical" ] || {
140
- echo "Recordings.app installation requires HOME to be an absolute canonical path." >&2
140
+ echo "HasnaRecordings.app installation requires HOME to be an absolute canonical path." >&2
141
141
  exit 1
142
142
  }
143
143
  [ "$("$STAT_EXECUTABLE" -f '%u' "$candidate")" = "$("$ID_EXECUTABLE" -u)" ] || {
@@ -352,7 +352,7 @@ require_absolute_executable "BUN_EXECUTABLE" "$BUN_EXECUTABLE"
352
352
  require_absolute_canonical_owned_home
353
353
 
354
354
  if [ "$("$UNAME_EXECUTABLE" -s)" != "Darwin" ]; then
355
- echo "Recordings.app installation is only supported on macOS." >&2
355
+ echo "HasnaRecordings.app installation is only supported on macOS." >&2
356
356
  exit 1
357
357
  fi
358
358
 
@@ -546,7 +546,7 @@ fi
546
546
  "$BUN_EXECUTABLE" "$ARTIFACT_TOOL" native-fs-guard-check
547
547
  DATA_DIR="${HOME}/.hasna/recordings"
548
548
  DATA_PARENT="$("$DIRNAME_EXECUTABLE" "$DATA_DIR")"
549
- MANIFEST_BUNDLE_NAME="Recordings.app"
549
+ MANIFEST_BUNDLE_NAME="HasnaRecordings.app"
550
550
  APP_DEST="${HOME}/Applications/${MANIFEST_BUNDLE_NAME}"
551
551
  APP_PARENT="$("$DIRNAME_EXECUTABLE" "$APP_DEST")"
552
552
  ROLLBACK_DIR="${DATA_DIR}/rollbacks"
@@ -794,7 +794,7 @@ acquire_install_lock() {
794
794
  if kill -0 "$owner_pid" 2>/dev/null; then
795
795
  actual_start="$("$PS_EXECUTABLE" -o lstart= -p "$owner_pid" 2>/dev/null | "$TR_EXECUTABLE" -s ' ' | "$SED_EXECUTABLE" 's/^ //;s/ $//' || true)"
796
796
  if [ -z "$owner_start" ] || [ -z "$actual_start" ] || [ "$owner_start" = "$actual_start" ]; then
797
- echo "Another Recordings.app installer owns the active install lock." >&2
797
+ echo "Another HasnaRecordings.app installer owns the active install lock." >&2
798
798
  exit 1
799
799
  fi
800
800
  fi
@@ -807,17 +807,17 @@ acquire_install_lock() {
807
807
  now="$("$DATE_EXECUTABLE" +%s)"
808
808
  lock_age=$((now - $("$STAT_EXECUTABLE" -f '%m' "$LOCK_DIR")))
809
809
  if [ "$lock_age" -lt "$stale_seconds" ]; then
810
- echo "Recordings.app install lock is incomplete and too recent to reclaim safely." >&2
810
+ echo "HasnaRecordings.app install lock is incomplete and too recent to reclaim safely." >&2
811
811
  exit 1
812
812
  fi
813
813
  fi
814
814
  stale_claim="${LOCK_DIR}.stale.$$"
815
815
  if ! "$MV_EXECUTABLE" "$LOCK_DIR" "$stale_claim" 2>/dev/null; then
816
- echo "Recordings.app install lock changed during stale-owner recovery." >&2
816
+ echo "HasnaRecordings.app install lock changed during stale-owner recovery." >&2
817
817
  exit 1
818
818
  fi
819
819
  "$RM_EXECUTABLE" -rf "$stale_claim"
820
- "$MKDIR_EXECUTABLE" -m 700 "$LOCK_DIR" || { echo "Could not acquire Recordings.app install lock." >&2; exit 1; }
820
+ "$MKDIR_EXECUTABLE" -m 700 "$LOCK_DIR" || { echo "Could not acquire HasnaRecordings.app install lock." >&2; exit 1; }
821
821
  fi
822
822
  LOCK_OWNED=1
823
823
  local start
@@ -1263,13 +1263,13 @@ stop_uncommitted_candidate() {
1263
1263
  done <<< "$remaining"
1264
1264
  "$SLEEP_EXECUTABLE" 0.1
1265
1265
  [ -z "$(pids_for_exact_executable "$expected")" ] || {
1266
- echo "Uncommitted Recordings.app process could not be stopped before recovery." >&2
1266
+ echo "Uncommitted HasnaRecordings.app process could not be stopped before recovery." >&2
1267
1267
  return 1
1268
1268
  }
1269
1269
  }
1270
1270
 
1271
1271
  if [ -f "$JOURNAL_PATH" ]; then
1272
- echo "Recovering incomplete Recordings.app installation transaction." >&2
1272
+ echo "Recovering incomplete HasnaRecordings.app installation transaction." >&2
1273
1273
  if ! RECOVER_WAS_RUNNING="$("$BUN_EXECUTABLE" "$ARTIFACT_TOOL" journal-get --journal "$JOURNAL_PATH" --field was_running)" || \
1274
1274
  ! RECOVER_PHASE="$("$BUN_EXECUTABLE" "$ARTIFACT_TOOL" journal-get --journal "$JOURNAL_PATH" --field phase)" || \
1275
1275
  ! RECOVER_RUNNING_APP_PATHS_JSON="$("$BUN_EXECUTABLE" "$ARTIFACT_TOOL" journal-get --journal "$JOURNAL_PATH" --field prior_running_app_paths)"; then
@@ -1376,7 +1376,7 @@ if ! "$BUN_EXECUTABLE" -e '
1376
1376
  if ((actual[i] ?? 0) < (minimum[i] ?? 0)) process.exit(1);
1377
1377
  }
1378
1378
  ' "$CURRENT_MACOS" "$MINIMUM_MACOS"; then
1379
- echo "Recordings.app requires macOS ${MINIMUM_MACOS} or later; target is ${CURRENT_MACOS}." >&2
1379
+ echo "HasnaRecordings.app requires macOS ${MINIMUM_MACOS} or later; target is ${CURRENT_MACOS}." >&2
1380
1380
  exit 1
1381
1381
  fi
1382
1382
  TARGET_ARCH="$("$UNAME_EXECUTABLE" -m)"
@@ -1386,7 +1386,7 @@ MANIFEST_ARCHITECTURES="$("$BUN_EXECUTABLE" "$ARTIFACT_TOOL" manifest-get \
1386
1386
  --field architectures)"
1387
1387
  case " ${MANIFEST_ARCHITECTURES} " in
1388
1388
  *" ${TARGET_ARCH} "*) ;;
1389
- *) echo "Recordings.app artifact does not support target architecture ${TARGET_ARCH}." >&2; exit 1 ;;
1389
+ *) echo "HasnaRecordings.app artifact does not support target architecture ${TARGET_ARCH}." >&2; exit 1 ;;
1390
1390
  esac
1391
1391
 
1392
1392
  verify_secure_parent "$APP_PARENT"
@@ -1412,7 +1412,7 @@ add_unique_app() {
1412
1412
  local existing
1413
1413
  if [ -e "$candidate" ] || [ -L "$candidate" ]; then
1414
1414
  [ -d "$candidate" ] && [ ! -L "$candidate" ] || {
1415
- echo "Recordings.app path is not a secure directory: ${candidate}" >&2
1415
+ echo "HasnaRecordings.app path is not a secure directory: ${candidate}" >&2
1416
1416
  exit 1
1417
1417
  }
1418
1418
  fi
@@ -1524,7 +1524,7 @@ stop_old_processes() {
1524
1524
  "$SLEEP_EXECUTABLE" 0.1
1525
1525
  done
1526
1526
  if [ "$remaining" -ne 0 ]; then
1527
- echo "Existing Recordings.app process did not stop before replacement." >&2
1527
+ echo "Existing HasnaRecordings.app process did not stop before replacement." >&2
1528
1528
  exit 1
1529
1529
  fi
1530
1530
  }
@@ -1545,7 +1545,7 @@ quiesce_old_processes() {
1545
1545
  fi
1546
1546
  "$SLEEP_EXECUTABLE" 0.1
1547
1547
  done
1548
- echo "Existing Recordings.app processes did not remain quiescent before replacement." >&2
1548
+ echo "Existing HasnaRecordings.app processes did not remain quiescent before replacement." >&2
1549
1549
  exit 1
1550
1550
  }
1551
1551
 
@@ -1727,7 +1727,7 @@ for existing_app in ${DISCOVERED_APPS[@]+"${DISCOVERED_APPS[@]}"}; do
1727
1727
  MANAGEABLE_APPS+=("$existing_app")
1728
1728
  ;;
1729
1729
  *)
1730
- echo "Duplicate Recordings.app at ${existing_app} is outside the transactional user install paths; archive or remove it before installing." >&2
1730
+ echo "Duplicate HasnaRecordings.app at ${existing_app} is outside the transactional user install paths; archive or remove it before installing." >&2
1731
1731
  exit 1
1732
1732
  ;;
1733
1733
  esac
@@ -1954,7 +1954,7 @@ for ((refresh_attempt = 1; refresh_attempt <= 5; refresh_attempt++)); do
1954
1954
  fi
1955
1955
  done
1956
1956
  [ "$STOPPED_REFRESH_STABLE" -eq 1 ] || {
1957
- echo "Existing Recordings.app writers did not remain quiescent around the stopped-state refresh." >&2
1957
+ echo "Existing HasnaRecordings.app writers did not remain quiescent around the stopped-state refresh." >&2
1958
1958
  exit 1
1959
1959
  }
1960
1960
 
@@ -2004,7 +2004,7 @@ for ((move_index = 0; move_index < MOVED_ORIGINAL_COUNT; move_index++)); do
2004
2004
  existing_app="${MOVED_ORIGINALS[$move_index]}"
2005
2005
  moved_path="${MOVED_PATHS[$move_index]}"
2006
2006
  [ "$("$BUN_EXECUTABLE" "$ARTIFACT_TOOL" tree-digest --path "$existing_app")" = "${MOVED_DIGESTS[$move_index]}" ] || {
2007
- echo "Installed Recordings.app changed after transactional backup planning." >&2
2007
+ echo "Installed HasnaRecordings.app changed after transactional backup planning." >&2
2008
2008
  exit 1
2009
2009
  }
2010
2010
  RECORDINGS_TEST_INSTALLER_PID="$$" \
@@ -2117,7 +2117,7 @@ if [ "$LAUNCH_APP" -eq 1 ] || [ "$was_running" -eq 1 ]; then
2117
2117
  fi
2118
2118
 
2119
2119
  if [ "$ARTIFACT_POLICY" = "local_only" ]; then
2120
- echo "Installed local-only Recordings.app for ${APPROVED_TARGET}; this artifact is ad-hoc signed and non-notarized."
2120
+ echo "Installed local-only HasnaRecordings.app for ${APPROVED_TARGET}; this artifact is ad-hoc signed and non-notarized."
2121
2121
  if [ "$identity_migration" -eq 1 ]; then
2122
2122
  # Not a maybe: the designated requirement did change, under an explicit approval.
2123
2123
  echo "Code identity changed under --allow-adhoc-identity-migration; the Microphone and Accessibility grants held by the replaced app no longer apply and must be reauthorized."
@@ -2127,5 +2127,5 @@ if [ "$ARTIFACT_POLICY" = "local_only" ]; then
2127
2127
  elif [ "$identity_migration" -eq 1 ]; then
2128
2128
  echo "Installed a new signing identity; macOS will require one-time permission approval for this migration."
2129
2129
  else
2130
- echo "Installed verified Recordings.app release artifact: ${APP_DEST}"
2130
+ echo "Installed verified HasnaRecordings.app release artifact: ${APP_DEST}"
2131
2131
  fi
@@ -183,8 +183,8 @@ export type BuildProvenance = {
183
183
 
184
184
  export type MacOSArtifactManifest = BuildProvenance & {
185
185
  artifact_type: "recordings-macos-app";
186
- /// The top-level bundle directory name inside the artifact (Recordings.app for the
187
- /// full app, HasnaRecordings.app for the bar variant per the fleet naming rule).
186
+ /// The top-level bundle directory name inside the artifact (HasnaRecordings.app for
187
+ /// both the full app and the bar variant per the fleet naming rule).
188
188
  bundle_name: string;
189
189
  app_sha256: string;
190
190
  binding: {
@@ -843,7 +843,7 @@ function manifestVariant(manifest: MacOSArtifactManifest): ArtifactVariant {
843
843
  }
844
844
 
845
845
  function manifestBundleName(manifest: MacOSArtifactManifest): string {
846
- return manifest.bundle_name ?? "Recordings.app";
846
+ return manifest.bundle_name ?? "HasnaRecordings.app";
847
847
  }
848
848
 
849
849
  function manifestTargetIdentityKind(manifest: MacOSArtifactManifest): OperatorTargetIdentityKind {
@@ -1094,23 +1094,23 @@ function assertCanonicalZipEntries(entries: Array<{ name: string; isDirectory: b
1094
1094
  const components = logicalPath.split("/");
1095
1095
  if (
1096
1096
  !logicalPath ||
1097
- components[0] !== "Recordings.app" ||
1097
+ components[0] !== "HasnaRecordings.app" ||
1098
1098
  components.some((component) => !component || component === "." || component === "..")
1099
1099
  ) {
1100
- throw new Error("release ZIP contains an entry outside the canonical Recordings.app tree");
1100
+ throw new Error("release ZIP contains an entry outside the canonical HasnaRecordings.app tree");
1101
1101
  }
1102
1102
  const collisionKey = canonicalZipCollisionKey(logicalPath);
1103
1103
  if (logicalPaths.has(collisionKey)) {
1104
1104
  throw new Error("release ZIP contains duplicate, file/directory, case-fold, or Unicode-colliding entries");
1105
1105
  }
1106
1106
  logicalPaths.set(collisionKey, { name: logicalPath, isDirectory: entry.isDirectory });
1107
- if (logicalPath === "Recordings.app") {
1108
- if (!entry.isDirectory) throw new Error("release ZIP Recordings.app root is not a directory entry");
1107
+ if (logicalPath === "HasnaRecordings.app") {
1108
+ if (!entry.isDirectory) throw new Error("release ZIP HasnaRecordings.app root is not a directory entry");
1109
1109
  rootDirectoryCount += 1;
1110
1110
  }
1111
1111
  }
1112
1112
  if (rootDirectoryCount !== 1) {
1113
- throw new Error("release ZIP must contain exactly one canonical Recordings.app root entry");
1113
+ throw new Error("release ZIP must contain exactly one canonical HasnaRecordings.app root entry");
1114
1114
  }
1115
1115
  for (const { name } of logicalPaths.values()) {
1116
1116
  const components = name.split("/");
@@ -1426,7 +1426,7 @@ export function withPrivatelyExtractedArchiveApp<T>(
1426
1426
  operation: (appPath: string) => T,
1427
1427
  platformArchiveTool = "/usr/bin/ditto",
1428
1428
  expectedArchiveSha256?: string,
1429
- expectedBundleName = "Recordings.app",
1429
+ expectedBundleName = "HasnaRecordings.app",
1430
1430
  ): T {
1431
1431
  const privateRoot = mkdtempSync(join(tmpdir(), "recordings-artifact-extract-"));
1432
1432
  chmodSync(privateRoot, 0o700);
@@ -1751,8 +1751,8 @@ export function assertManifestShape(manifest: MacOSArtifactManifest): void {
1751
1751
  manifest.container?.type !== "zip" ||
1752
1752
  JSON.stringify(manifest.container.install_locations) !== JSON.stringify(
1753
1753
  artifactPolicy === "release"
1754
- ? ["/Applications/Recordings.app"]
1755
- : ["~/Applications/Recordings.app"],
1754
+ ? ["/Applications/HasnaRecordings.app"]
1755
+ : ["~/Applications/HasnaRecordings.app"],
1756
1756
  )
1757
1757
  ) {
1758
1758
  throw new Error("manifest has an unexpected container install policy");
@@ -2516,7 +2516,7 @@ export function assertVersionTransition(
2516
2516
  assertManifestShape(manifest);
2517
2517
  if (compareVersions(manifest.bundle_version, installedVersion) < 0) {
2518
2518
  throw new Error(
2519
- `refusing to downgrade Recordings.app from ${installedVersion} to ${manifest.bundle_version}`,
2519
+ `refusing to downgrade HasnaRecordings.app from ${installedVersion} to ${manifest.bundle_version}`,
2520
2520
  );
2521
2521
  }
2522
2522
  if (compareVersions(manifest.bundle_version, installedVersion) === 0) {
@@ -2626,8 +2626,6 @@ function verifyActiveApp(
2626
2626
  const environment: NodeJS.ProcessEnv = {
2627
2627
  HOME: contractHome,
2628
2628
  PATH: "/usr/bin:/bin:/usr/sbin:/sbin",
2629
- HASNA_RECORDINGS_CLIENT_STORE: "sqlite",
2630
- RECORDINGS_CLIENT_STORE: "sqlite",
2631
2629
  HASNA_RECORDINGS_DB_PATH: join(contractHome, "recordings.db"),
2632
2630
  RECORDINGS_AUDIO_DIR: join(contractHome, "audio"),
2633
2631
  };
@@ -2983,7 +2981,7 @@ function readJournal(path: string): InstallJournal {
2983
2981
  if (resolve(path) !== resolve(join(expectedParent, ".Recordings-install-transaction.json"))) {
2984
2982
  throw new Error("install transaction journal is outside the expected app parent");
2985
2983
  }
2986
- if (resolve(journal.app_destination) !== resolve(join(expectedParent, "Recordings.app"))) {
2984
+ if (resolve(journal.app_destination) !== resolve(join(expectedParent, "HasnaRecordings.app"))) {
2987
2985
  throw new Error("install transaction journal has an unexpected app destination");
2988
2986
  }
2989
2987
  const expectedDataDir = resolve(join(dirname(expectedParent), ".hasna", "recordings"));
@@ -3001,7 +2999,9 @@ function readJournal(path: string): InstallJournal {
3001
2999
  return (
3002
3000
  resolved === resolve(journal.app_destination) ||
3003
3001
  resolved === resolve(join(journal.data_dir, "Recordings.app")) ||
3004
- resolved.startsWith(`${resolve(journal.app_parent)}/Recordings.app.`)
3002
+ resolved === resolve(join(journal.data_dir, "HasnaRecordings.app")) ||
3003
+ resolved.startsWith(`${resolve(journal.app_parent)}/Recordings.app.`) ||
3004
+ resolved.startsWith(`${resolve(journal.app_parent)}/HasnaRecordings.app.`)
3005
3005
  );
3006
3006
  };
3007
3007
  if (!journal.originals.every((entry) => allowedOriginal(entry.path))) {
@@ -3939,7 +3939,7 @@ function openRecoveryCapabilities(
3939
3939
  const homePath = resolve(dirname(appParent));
3940
3940
  if (
3941
3941
  basename(appParent) !== "Applications" ||
3942
- resolve(journal.app_destination) !== resolve(join(appParent, "Recordings.app")) ||
3942
+ resolve(journal.app_destination) !== resolve(join(appParent, "HasnaRecordings.app")) ||
3943
3943
  resolve(journal.data_dir) !== resolve(join(homePath, ".hasna", "recordings")) ||
3944
3944
  dirname(resolve(journal.transaction_dir)) !== appParent
3945
3945
  ) {
@@ -4342,9 +4342,10 @@ export function publishInstallCandidate(
4342
4342
  const destination = resolve(destinationPath);
4343
4343
  const stagingParentPath = dirname(staging);
4344
4344
  const stagingParentLeaf = basename(stagingParentPath);
4345
+ const bundleLeaf = basename(journal.app_destination);
4345
4346
  if (
4346
4347
  destination !== resolve(journal.app_destination) ||
4347
- basename(staging) !== "Recordings.app" ||
4348
+ basename(staging) !== bundleLeaf ||
4348
4349
  dirname(stagingParentPath) !== resolve(journal.app_parent) ||
4349
4350
  !/^\.Recordings-install\.[A-Za-z0-9]+$/.test(stagingParentLeaf)
4350
4351
  ) {
@@ -4367,12 +4368,12 @@ export function publishInstallCandidate(
4367
4368
  stagingParent,
4368
4369
  "candidate staging parent",
4369
4370
  );
4370
- retained = guard.openDirAt(stagingParent, "Recordings.app");
4371
- assertNativeBinding(guard, stagingParent, "Recordings.app", retained, "staged candidate");
4371
+ retained = guard.openDirAt(stagingParent, bundleLeaf);
4372
+ assertNativeBinding(guard, stagingParent, bundleLeaf, retained, "staged candidate");
4372
4373
  if (nativeTreeDigest(guard, retained) !== expectedDigest) {
4373
4374
  throw new Error("staged candidate does not match durable journal evidence");
4374
4375
  }
4375
- if (guard.statAt(capabilities.applications, "Recordings.app") !== null) {
4376
+ if (guard.statAt(capabilities.applications, bundleLeaf) !== null) {
4376
4377
  throw new Error("candidate destination already exists");
4377
4378
  }
4378
4379
  fsyncRetainedTree(guard, retained, uid, "staged candidate");
@@ -4384,8 +4385,8 @@ export function publishInstallCandidate(
4384
4385
  stagingParent,
4385
4386
  "candidate staging parent",
4386
4387
  );
4387
- assertNativeBinding(guard, stagingParent, "Recordings.app", retained, "staged candidate");
4388
- if (guard.statAt(capabilities.applications, "Recordings.app") !== null) {
4388
+ assertNativeBinding(guard, stagingParent, bundleLeaf, retained, "staged candidate");
4389
+ if (guard.statAt(capabilities.applications, bundleLeaf) !== null) {
4389
4390
  throw new Error("candidate destination appeared before publication");
4390
4391
  }
4391
4392
  installTransitionTestPoint(
@@ -4395,10 +4396,10 @@ export function publishInstallCandidate(
4395
4396
  );
4396
4397
  guard.renameHandleNoReplaceAt(
4397
4398
  stagingParent,
4398
- "Recordings.app",
4399
+ bundleLeaf,
4399
4400
  retained,
4400
4401
  capabilities.applications,
4401
- "Recordings.app",
4402
+ bundleLeaf,
4402
4403
  );
4403
4404
  installTransitionTestPoint(
4404
4405
  "publish-candidate",
@@ -4409,11 +4410,11 @@ export function publishInstallCandidate(
4409
4410
  assertNativeBinding(
4410
4411
  guard,
4411
4412
  capabilities.applications,
4412
- "Recordings.app",
4413
+ bundleLeaf,
4413
4414
  retained,
4414
4415
  "published candidate",
4415
4416
  );
4416
- if (guard.statAt(stagingParent, "Recordings.app") !== null) {
4417
+ if (guard.statAt(stagingParent, bundleLeaf) !== null) {
4417
4418
  throw new Error("candidate staging leaf was recreated during publication");
4418
4419
  }
4419
4420
  guard.fsyncHandle(capabilities.applications);
@@ -4459,7 +4460,8 @@ function cleanupInstallCandidateStaging(
4459
4460
  staging,
4460
4461
  "candidate staging root",
4461
4462
  );
4462
- const candidateMetadata = guard.statAt(staging, "Recordings.app");
4463
+ const bundleLeaf = basename(journal.app_destination);
4464
+ const candidateMetadata = guard.statAt(staging, bundleLeaf);
4463
4465
  if (candidateMetadata !== null) {
4464
4466
  if (journal.schema_version !== 9 || !journal.candidate_tree_sha256) {
4465
4467
  throw new Error("legacy journal cannot authenticate candidate staging cleanup");
@@ -4467,7 +4469,7 @@ function cleanupInstallCandidateStaging(
4467
4469
  const candidate = openProvenDirectoryAt(
4468
4470
  guard,
4469
4471
  staging,
4470
- "Recordings.app",
4472
+ bundleLeaf,
4471
4473
  journal.candidate_tree_sha256,
4472
4474
  "staged candidate",
4473
4475
  );
@@ -4475,7 +4477,7 @@ function cleanupInstallCandidateStaging(
4475
4477
  quarantineRemoveRetainedAt(
4476
4478
  guard,
4477
4479
  staging,
4478
- "Recordings.app",
4480
+ bundleLeaf,
4479
4481
  candidate,
4480
4482
  "staged candidate",
4481
4483
  );
@@ -4567,14 +4569,20 @@ function originalDestinationCapability(
4567
4569
  ): { parent: NativeHandle; leaf: string } {
4568
4570
  const resolved = resolve(path);
4569
4571
  if (resolved === resolve(journal.app_destination)) {
4570
- return { parent: capabilities.applications, leaf: "Recordings.app" };
4572
+ return { parent: capabilities.applications, leaf: basename(journal.app_destination) };
4571
4573
  }
4572
- if (resolved === resolve(join(journal.data_dir, "Recordings.app"))) {
4573
- return { parent: capabilities.data, leaf: "Recordings.app" };
4574
+ if (
4575
+ dirname(resolved) === resolve(journal.data_dir) &&
4576
+ (basename(resolved) === "Recordings.app" || basename(resolved) === "HasnaRecordings.app")
4577
+ ) {
4578
+ // Install site inside the state data directory: the legacy pre-rename
4579
+ // Recordings.app and the renamed HasnaRecordings.app are both recognized and
4580
+ // preserved, never treated as the canonical Applications destination.
4581
+ return { parent: capabilities.data, leaf: basename(resolved) };
4574
4582
  }
4575
4583
  if (
4576
4584
  dirname(resolved) === resolve(journal.app_parent) &&
4577
- /^Recordings\.app\.[A-Za-z0-9._-]+$/.test(basename(resolved))
4585
+ /^(?:Hasna)?Recordings\.app\.[A-Za-z0-9._-]+$/.test(basename(resolved))
4578
4586
  ) {
4579
4587
  return { parent: capabilities.applications, leaf: basename(resolved) };
4580
4588
  }
@@ -5005,24 +5013,25 @@ function recoverJournal(path: string): void {
5005
5013
  const canonicalOriginal = journal.originals.find(
5006
5014
  (entry) => resolve(entry.path) === resolve(journal.app_destination),
5007
5015
  );
5016
+ const destinationLeaf = basename(journal.app_destination);
5008
5017
  const canonicalDestinationDigest = canonicalOriginal
5009
- ? nativeTreeDigestAt(guard, applications, "Recordings.app")
5018
+ ? nativeTreeDigestAt(guard, applications, destinationLeaf)
5010
5019
  : null;
5011
5020
  const canonicalAlreadyRestored = canonicalOriginal !== undefined &&
5012
5021
  canonicalDestinationDigest === canonicalOriginal.sha256;
5013
5022
  if (
5014
5023
  ["candidate-moving", "candidate-installed", "activated", "launching"].includes(journal.phase) &&
5015
- !canonicalAlreadyRestored && guard.statAt(applications, "Recordings.app") !== null
5024
+ !canonicalAlreadyRestored && guard.statAt(applications, destinationLeaf) !== null
5016
5025
  ) {
5017
5026
  if (journal.schema_version !== 9 || !journal.candidate_tree_sha256) {
5018
5027
  throw new Error("legacy recovery journal cannot prove the uncommitted candidate tree");
5019
5028
  }
5020
- recoveryTestBarrier("before-candidate-remove", "Recordings.app");
5029
+ recoveryTestBarrier("before-candidate-remove", destinationLeaf);
5021
5030
  assertNativeBinding(guard, capabilities.home, "Applications", applications, "Applications");
5022
5031
  const candidate = openProvenDirectoryAt(
5023
5032
  guard,
5024
5033
  applications,
5025
- "Recordings.app",
5034
+ destinationLeaf,
5026
5035
  journal.candidate_tree_sha256,
5027
5036
  "uncommitted candidate",
5028
5037
  );
@@ -5030,7 +5039,7 @@ function recoverJournal(path: string): void {
5030
5039
  quarantineRemoveRetainedAt(
5031
5040
  guard,
5032
5041
  applications,
5033
- "Recordings.app",
5042
+ destinationLeaf,
5034
5043
  candidate,
5035
5044
  "uncommitted candidate",
5036
5045
  );