@hasna/recordings 0.3.1 → 0.3.8

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 (63) hide show
  1. package/README.md +38 -34
  2. package/bun.lock +96 -36
  3. package/dist/__tests__/helpers/installer-guard-execution.d.ts.map +1 -1
  4. package/dist/__tests__/helpers/source-assertions.d.ts +6 -4
  5. package/dist/__tests__/helpers/source-assertions.d.ts.map +1 -1
  6. package/dist/cli/index.js +197 -95
  7. package/dist/cli/macos-shortcut.d.ts +2 -2
  8. package/dist/db/pg-migrations.d.ts +1 -1
  9. package/dist/http/client.d.ts +0 -1
  10. package/dist/http/client.d.ts.map +1 -1
  11. package/dist/index.js +162 -73
  12. package/dist/lib/capture-probe.d.ts +3 -3
  13. package/dist/lib/capture-probe.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 +144 -59
  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 +168 -110
  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 +144 -60
  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 +75 -28
  40. package/scripts/macos_artifact.ts +162 -45
  41. package/scripts/migrate.ts +2 -2
  42. package/scripts/release-suite-gate.ts +181 -0
  43. package/scripts/set-version.ts +5 -5
  44. package/scripts/smoke_macos_app.sh +80 -34
  45. package/scripts/vacuity-manifests/version-sites.tsv +8 -4
  46. package/src/native/Recordings/App/MenuBarStatusView.swift +9 -4
  47. package/src/native/Recordings/App/RecordingsApp.swift +37 -1
  48. package/src/native/Recordings/RecordingsLib/Info.plist +3 -3
  49. package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +21 -1
  50. package/src/native/Recordings/RecordingsTests/BarOnlyLaunchPlanTests.swift +99 -0
  51. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +2 -2
  52. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +1 -1
  53. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +1 -1
  54. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +2 -2
  55. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +1 -1
  56. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +1 -1
  57. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +1 -1
  58. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +2 -2
  59. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +1 -1
  60. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +2 -2
  61. package/src/native/Recordings/build.sh +152 -18
  62. package/dist/lib/retired-deployment-modes.d.ts +0 -24
  63. package/dist/lib/retired-deployment-modes.d.ts.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1925,9 +1925,118 @@ function setAgentFocus(idOrName, projectId, db) {
1925
1925
  d.query("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?").run(resolvedProjectId, new Date().toISOString(), agent.id);
1926
1926
  return getAgent(agent.id, d);
1927
1927
  }
1928
+ // package.json
1929
+ var package_default = {
1930
+ name: "@hasna/recordings",
1931
+ version: "0.3.8",
1932
+ type: "module",
1933
+ description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
1934
+ repository: {
1935
+ type: "git",
1936
+ url: "git+https://github.com/hasna/apps"
1937
+ },
1938
+ main: "dist/index.js",
1939
+ types: "dist/index.d.ts",
1940
+ bin: {
1941
+ recordings: "dist/cli/index.js",
1942
+ "recordings-mcp": "dist/mcp/index.js",
1943
+ "recordings-serve": "dist/server/index.js"
1944
+ },
1945
+ exports: {
1946
+ ".": {
1947
+ import: "./dist/index.js",
1948
+ types: "./dist/index.d.ts"
1949
+ },
1950
+ "./storage": {
1951
+ import: "./dist/storage.js",
1952
+ types: "./dist/storage.d.ts"
1953
+ },
1954
+ "./sdk": {
1955
+ import: "./dist/sdk/index.js",
1956
+ types: "./dist/sdk/index.d.ts"
1957
+ }
1958
+ },
1959
+ engines: {
1960
+ bun: ">=1.0.0"
1961
+ },
1962
+ scripts: {
1963
+ clean: "rm -rf dist",
1964
+ build: "bun run clean && bun run build:cli && bun run build:mcp && bun run build:serve && bun run build:lib && tsc --emitDeclarationOnly --outDir dist",
1965
+ "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai",
1966
+ "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai",
1967
+ "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg",
1968
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai",
1969
+ "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
1970
+ "generate:sdk": "bun run scripts/generate-sdk.ts",
1971
+ migrate: "bun run scripts/migrate.ts",
1972
+ typecheck: "tsc --noEmit",
1973
+ "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
1974
+ test: "bun test",
1975
+ "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
1976
+ "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
1977
+ "verify:ci-native": "bun scripts/ci-native-build.ts",
1978
+ "test:gated": 'bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS="${RECORDINGS_TEST_TIMEOUT_MS:-120000}" bun test --timeout "$RECORDINGS_TEST_TIMEOUT_MS" $(bun scripts/ci-linux-suite.ts --gated)',
1979
+ "test:vacuity-battery": 'bun scripts/vacuity-manifest-gen.ts > "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv" && bun scripts/vacuity-mutation-battery.ts "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv"',
1980
+ "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
1981
+ "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
1982
+ "test:vacuity-battery:vars": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/variable-operands.tsv",
1983
+ "test:vacuity-battery:reorder": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/reorder.tsv",
1984
+ "test:coverage": "bun test --coverage",
1985
+ "dev:cli": "bun run src/cli/index.ts",
1986
+ "desktop:snapshot": "bun run src/cli/index.ts app snapshot",
1987
+ "dev:mcp": "bun run src/mcp/index.ts",
1988
+ "verify:release": "bun run scripts/release-guard.ts",
1989
+ "scan:artifact": "bun run scripts/scan-artifact.ts",
1990
+ "version:set": "bun run scripts/set-version.ts",
1991
+ "version:check": "bun run scripts/set-version.ts --check",
1992
+ prepack: "bun run prepack:platform-gate && bun run version:check && bun run build && bun run verify:release && bun run scan:artifact",
1993
+ "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) \u2014 dry-run/CI pack; publish recordings from macOS"; fi'`,
1994
+ prepublishOnly: "bun run typecheck && bun run release-suite-gate",
1995
+ "release-suite-gate": "bun run scripts/release-suite-gate.ts",
1996
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
1997
+ },
1998
+ files: [
1999
+ "dist/",
2000
+ "scripts/",
2001
+ "Dockerfile.package",
2002
+ "bun.lock",
2003
+ "src/native/Recordings/App/",
2004
+ "src/native/Recordings/RecordingsLib/",
2005
+ "src/native/Recordings/RecordingsTests/",
2006
+ "src/native/Recordings/Updater/",
2007
+ "src/native/Recordings/Package.swift",
2008
+ "src/native/Recordings/Package.resolved",
2009
+ "src/native/Recordings/build.sh",
2010
+ "packaging/macos/",
2011
+ "README.md",
2012
+ "LICENSE"
2013
+ ],
2014
+ dependencies: {
2015
+ "@hasna/contracts": "0.13.3",
2016
+ "@hasna/events": "0.1.11",
2017
+ "@modelcontextprotocol/sdk": "^1.12.1",
2018
+ chalk: "^5.4.1",
2019
+ commander: "^13.1.0",
2020
+ openai: "^5.1.0",
2021
+ pg: "^8.13.3",
2022
+ zod: "^3.24.2"
2023
+ },
2024
+ devDependencies: {
2025
+ "@types/bun": "^1.2.5",
2026
+ "@types/pg": "^8.11.11",
2027
+ "node-api-headers": "1.9.0",
2028
+ typescript: "^5.8.2"
2029
+ },
2030
+ publishConfig: {
2031
+ registry: "https://registry.npmjs.org",
2032
+ access: "public"
2033
+ },
2034
+ license: "Apache-2.0",
2035
+ author: "Hasna <andrei@hasna.com>"
2036
+ };
1928
2037
 
1929
2038
  // src/version.ts
1930
- var VERSION = "0.3.1";
2039
+ var VERSION = package_default.version;
1931
2040
 
1932
2041
  // src/db/feedback.ts
1933
2042
  function saveFeedback(input) {
@@ -1935,58 +2044,26 @@ function saveFeedback(input) {
1935
2044
  db.query("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)").run(input.message, input.email ?? null, input.category ?? "general", input.version ?? VERSION);
1936
2045
  }
1937
2046
 
1938
- // src/lib/retired-deployment-modes.ts
1939
- var RETIRED_DEPLOYMENT_MODES = [
1940
- "local",
1941
- "self_hosted",
1942
- "cloud",
1943
- "remote",
1944
- "hybrid"
1945
- ];
1946
- function normalizeModeToken(value) {
1947
- return value.trim().toLowerCase().replace(/-/g, "_");
1948
- }
1949
- function asRetiredDeploymentMode(value) {
1950
- const token = normalizeModeToken(value);
1951
- return RETIRED_DEPLOYMENT_MODES.includes(token) ? token : null;
1952
- }
1953
- function retiredDeploymentModeError(rawValue, sourceEnvKey, replacement) {
1954
- const mode = asRetiredDeploymentMode(rawValue);
1955
- const value = mode === "local" ? replacement.onBox : replacement.offBox;
1956
- return new Error(`${sourceEnvKey}=${rawValue} names a deployment mode, and deployment modes are removed: ` + `${RETIRED_DEPLOYMENT_MODES.join(" | ")} no longer select anything. ` + `Set ${replacement.envKey}=${value} instead ` + `(${replacement.envKey} takes ${replacement.onBox} or ${replacement.offBox}).`);
1957
- }
1958
-
1959
2047
  // src/http/client.ts
1960
- var SERVER_BACKEND_VALUES = ["sqlite", "postgresql", "postgres"];
1961
2048
  function envToken(name) {
1962
2049
  return name.toUpperCase().replace(/-/g, "_");
1963
2050
  }
1964
- function clientStoreReplacement(envKey) {
1965
- return { envKey, onBox: "sqlite", offBox: "http" };
2051
+ function envKeys(name) {
2052
+ const token = envToken(name);
2053
+ return {
2054
+ storeKeys: [`HASNA_${token}_CLIENT_STORE`, `${token}_CLIENT_STORE`],
2055
+ apiUrlKeys: [`HASNA_${token}_API_URL`],
2056
+ apiKeyKeys: [`HASNA_${token}_API_KEY`]
2057
+ };
1966
2058
  }
1967
- function normalizeClientStore(value, sourceEnvKey, replacementEnvKey) {
1968
- const normalized = normalizeModeToken(value);
2059
+ function normalizeClientStore(value) {
2060
+ const normalized = value.trim().toLowerCase();
1969
2061
  if (normalized === "sqlite")
1970
2062
  return "sqlite";
1971
2063
  if (normalized === "http" || normalized === "https")
1972
2064
  return "http";
1973
- if (asRetiredDeploymentMode(value)) {
1974
- throw retiredDeploymentModeError(value, sourceEnvKey, clientStoreReplacement(replacementEnvKey));
1975
- }
1976
2065
  throw new Error(`Unknown client store: ${value}. Use sqlite or http.`);
1977
2066
  }
1978
- function defaultApiBaseUrl(name) {
1979
- return `https://${name}.hasna.xyz`;
1980
- }
1981
- function envKeys(name) {
1982
- const token = envToken(name);
1983
- return {
1984
- storeKeys: [`HASNA_${token}_CLIENT_STORE`, `${token}_CLIENT_STORE`],
1985
- retiredModeKeys: [`HASNA_${token}_STORAGE_MODE`, `HASNA_${token}_MODE`, `${token}_STORAGE_MODE`, `${token}_MODE`],
1986
- apiUrlKeys: [`HASNA_${token}_API_URL`, `${token}_API_URL`],
1987
- apiKeyKeys: [`HASNA_${token}_API_KEY`, `${token}_API_KEY`]
1988
- };
1989
- }
1990
2067
  function firstEnv(env, keys) {
1991
2068
  for (const key of keys) {
1992
2069
  const value = env[key]?.trim();
@@ -1995,20 +2072,6 @@ function firstEnv(env, keys) {
1995
2072
  }
1996
2073
  return null;
1997
2074
  }
1998
- function assertNoRetiredMode(env, keys) {
1999
- for (const key of keys.retiredModeKeys) {
2000
- const value = env[key]?.trim();
2001
- if (!value)
2002
- continue;
2003
- const normalized = normalizeModeToken(value);
2004
- if (SERVER_BACKEND_VALUES.includes(normalized))
2005
- continue;
2006
- if (asRetiredDeploymentMode(value)) {
2007
- throw retiredDeploymentModeError(value, key, clientStoreReplacement(keys.storeKeys[0]));
2008
- }
2009
- throw new Error(`${key}=${value} is not a client store. ${key} no longer selects one; ` + `set ${keys.storeKeys[0]}=sqlite or ${keys.storeKeys[0]}=http instead.`);
2010
- }
2011
- }
2012
2075
  function toV1BaseUrl(apiUrl) {
2013
2076
  const url = new URL(apiUrl);
2014
2077
  if (url.protocol !== "http:" && url.protocol !== "https:") {
@@ -2024,22 +2087,44 @@ function toV1BaseUrl(apiUrl) {
2024
2087
  }
2025
2088
  function resolveTransport(name, env = process.env) {
2026
2089
  const keys = envKeys(name);
2027
- assertNoRetiredMode(env, keys);
2028
2090
  const storeHit = firstEnv(env, keys.storeKeys);
2029
2091
  const urlHit = firstEnv(env, keys.apiUrlKeys);
2030
2092
  const keyHit = firstEnv(env, keys.apiKeyKeys);
2031
2093
  let requested = "sqlite";
2032
2094
  let modeSource = "default";
2033
2095
  if (storeHit) {
2034
- requested = normalizeClientStore(storeHit.value, storeHit.key, keys.storeKeys[0]);
2096
+ requested = normalizeClientStore(storeHit.value);
2035
2097
  modeSource = storeHit.key;
2036
2098
  } else if (urlHit && keyHit) {
2037
2099
  requested = "http";
2038
2100
  modeSource = "auto:api-url+api-key";
2101
+ } else if (urlHit || keyHit) {
2102
+ const missing = urlHit ? keys.apiKeyKeys[0] : keys.apiUrlKeys[0];
2103
+ const present = urlHit ? keys.apiUrlKeys[0] : keys.apiKeyKeys[0];
2104
+ return {
2105
+ transport: "sqlite",
2106
+ requested,
2107
+ modeSource,
2108
+ baseUrl: null,
2109
+ apiKeyPresent: Boolean(keyHit),
2110
+ misconfigured: true,
2111
+ warning: `${present} is set but ${missing} is not: the hosted API is only ` + `selected when BOTH are present. Set ${missing}, or unset ${present} to ` + `use the on-box store.`
2112
+ };
2039
2113
  }
2040
2114
  if (requested === "sqlite") {
2041
2115
  return { transport: "sqlite", requested, modeSource, baseUrl: null, apiKeyPresent: Boolean(keyHit), misconfigured: false, warning: null };
2042
2116
  }
2117
+ if (!urlHit) {
2118
+ return {
2119
+ transport: "sqlite",
2120
+ requested,
2121
+ modeSource,
2122
+ baseUrl: null,
2123
+ apiKeyPresent: Boolean(keyHit),
2124
+ misconfigured: true,
2125
+ warning: `${modeSource}=http but no API URL is set (${keys.apiUrlKeys[0]}). Refusing to route to the API.`
2126
+ };
2127
+ }
2043
2128
  if (!keyHit) {
2044
2129
  return {
2045
2130
  transport: "sqlite",
@@ -2051,7 +2136,7 @@ function resolveTransport(name, env = process.env) {
2051
2136
  warning: `${modeSource}=http but no API key is set (${keys.apiKeyKeys[0]}). Refusing to route to the API.`
2052
2137
  };
2053
2138
  }
2054
- const rawUrl = urlHit?.value ?? defaultApiBaseUrl(name);
2139
+ const rawUrl = urlHit.value;
2055
2140
  let baseUrl;
2056
2141
  try {
2057
2142
  baseUrl = toV1BaseUrl(rawUrl);
@@ -3003,37 +3088,37 @@ function captureProbeSubject(env = process.env, options = {}) {
3003
3088
  const termProgram = env.TERM_PROGRAM?.trim();
3004
3089
  const hasTty = options.hasTty ?? Boolean(process.stdin.isTTY || process.stdout.isTTY);
3005
3090
  if (overSsh) {
3006
- const subject2 = "the SSH session (sshd), not Recordings.app";
3091
+ const subject2 = "the SSH session (sshd), not HasnaRecordings.app";
3007
3092
  return {
3008
3093
  headless: true,
3009
3094
  subject_known: true,
3010
3095
  subject: subject2,
3011
- note: "Running over SSH: macOS cannot display a consent prompt to a session with no GUI, " + "so Microphone stays not_determined and a silent capture here says NOTHING about " + "whether Recordings.app can record. Judge the app by its own TCC entry and its log."
3096
+ note: "Running over SSH: macOS cannot display a consent prompt to a session with no GUI, " + "so Microphone stays not_determined and a silent capture here says NOTHING about " + "whether HasnaRecordings.app can record. Judge the app by its own TCC entry and its log."
3012
3097
  };
3013
3098
  }
3014
3099
  if (inTmux) {
3015
- const subject2 = "the tmux server, not Recordings.app and not this pane's shell";
3100
+ const subject2 = "the tmux server, not HasnaRecordings.app and not this pane's shell";
3016
3101
  return {
3017
3102
  headless: false,
3018
3103
  subject_known: true,
3019
3104
  subject: subject2,
3020
- note: "Running inside tmux: TCC attributes this capture to the tmux binary, which holds its " + "own grant. tmux also snapshots the environment at pane creation, so SSH variables may " + "be missing even in a remote session \u2014 treat a silent result as inconclusive about both " + "Recordings.app and about whether anyone could have been prompted."
3105
+ note: "Running inside tmux: TCC attributes this capture to the tmux binary, which holds its " + "own grant. tmux also snapshots the environment at pane creation, so SSH variables may " + "be missing even in a remote session \u2014 treat a silent result as inconclusive about both " + "HasnaRecordings.app and about whether anyone could have been prompted."
3021
3106
  };
3022
3107
  }
3023
3108
  if (!termProgram && !hasTty) {
3024
3109
  return {
3025
3110
  headless: true,
3026
3111
  subject_known: false,
3027
- subject: "an unidentified responsible process (not Recordings.app)",
3028
- note: "The responsible process could not be identified: no SSH variables, no TERM_PROGRAM and " + "no tty, which is what launchd, cron, CI and sudo look like. Whatever holds the grant, it " + "is not Recordings.app, and nothing here can be shown a consent prompt. Inconclusive."
3112
+ subject: "an unidentified responsible process (not HasnaRecordings.app)",
3113
+ note: "The responsible process could not be identified: no SSH variables, no TERM_PROGRAM and " + "no tty, which is what launchd, cron, CI and sudo look like. Whatever holds the grant, it " + "is not HasnaRecordings.app, and nothing here can be shown a consent prompt. Inconclusive."
3029
3114
  };
3030
3115
  }
3031
- const subject = `${termProgram || "the terminal application running this command"}, not Recordings.app`;
3116
+ const subject = `${termProgram || "the terminal application running this command"}, not HasnaRecordings.app`;
3032
3117
  return {
3033
3118
  headless: false,
3034
3119
  subject_known: Boolean(termProgram),
3035
3120
  subject,
3036
- note: `Grants are per responsible process: this probe exercises ${subject}. ` + "A pass proves the microphone hardware and the input device work; it does not " + "transfer to Recordings.app, which needs its own grant."
3121
+ note: `Grants are per responsible process: this probe exercises ${subject}. ` + "A pass proves the microphone hardware and the input device work; it does not " + "transfer to HasnaRecordings.app, which needs its own grant."
3037
3122
  };
3038
3123
  }
3039
3124
  var TCC_UNREADABLE_STATE = TCC_DATABASE_UNREADABLE_STATE;
@@ -3063,7 +3148,7 @@ function microphoneGrantInstruction(options) {
3063
3148
  const bundlePath = candidates[0] ?? null;
3064
3149
  const steps = [];
3065
3150
  if (!bundlePath) {
3066
- steps.push("No Recordings.app bundle was found on disk, so there is nothing to grant Microphone to yet. " + "Install the app first ('recordings app install').");
3151
+ steps.push("No HasnaRecordings.app bundle was found on disk, so there is nothing to grant Microphone to yet. " + "Install the app first ('recordings app install').");
3067
3152
  return {
3068
3153
  bundle_path: null,
3069
3154
  bundle_identifier: RECORDINGS_BUNDLE_IDENTIFIER,
@@ -3072,7 +3157,7 @@ function microphoneGrantInstruction(options) {
3072
3157
  };
3073
3158
  }
3074
3159
  if (candidates.length > 1) {
3075
- steps.push(`AMBIGUOUS: ${candidates.length} Recordings.app bundles exist (${candidates.join(", ")}). ` + "A TCC grant is bound to the bundle's code signature, so granting one does not grant the " + "other, and the toggle in Settings does not say which is which. Remove the bundles you are " + "not running before granting, or the grant may attach to the wrong one.");
3160
+ steps.push(`AMBIGUOUS: ${candidates.length} HasnaRecordings.app bundles exist (${candidates.join(", ")}). ` + "A TCC grant is bound to the bundle's code signature, so granting one does not grant the " + "other, and the toggle in Settings does not say which is which. Remove the bundles you are " + "not running before granting, or the grant may attach to the wrong one.");
3076
3161
  }
3077
3162
  steps.push(`At the keyboard on the machine itself (not over SSH), launch ${bundlePath} and start a ` + "recording once. macOS shows the consent sheet titled " + `"\u201CRecordings\u201D would like to access the microphone" \u2014 click Allow.`);
3078
3163
  steps.push("If no sheet appears, open System Settings \u2192 Privacy & Security \u2192 Microphone " + "and switch ON the row named \u201CRecordings\u201D. " + `That row is bundle ${RECORDINGS_BUNDLE_IDENTIFIER} at ${bundlePath}; the binary that ` + `receives the grant is ${join5(bundlePath, "Contents", "MacOS", "Recordings")}.`);
@@ -3168,7 +3253,7 @@ function describeActiveStore(config, env = process.env) {
3168
3253
  warnings.push(`writes go to ${safeBaseUrl(resolution.baseUrl)}, but ${localDbPath} still holds ` + `${localDbRecordings} recordings from an earlier on-box-only period. ` + "That file is NOT the live store \u2014 auditing it undercounts and looks like data loss.");
3169
3254
  }
3170
3255
  if (resolution.transport === "http" && resolution.modeSource === AUTO_FLIP_MODE_SOURCE) {
3171
- warnings.push("the API transport was selected by the mere PRESENCE of " + "HASNA_RECORDINGS_API_URL + HASNA_RECORDINGS_API_KEY, with no store variable set. " + "Check `launchctl getenv HASNA_RECORDINGS_API_URL` too: a launchd session variable " + "is inherited by the GUI app as well as by shells, and is invisible in a login profile.");
3256
+ warnings.push("the API transport was selected by the mere PRESENCE of " + "HASNA_RECORDINGS_API_URL + HASNA_RECORDINGS_API_KEY. " + "Check `launchctl getenv HASNA_RECORDINGS_API_URL` too: a launchd session variable " + "is inherited by the GUI app as well as by shells, and is invisible in a login profile.");
3172
3257
  }
3173
3258
  return {
3174
3259
  transport: resolution.transport,
@@ -5179,12 +5264,20 @@ program.command("init").description("Initialize .recordings/ in current director
5179
5264
  console.log(chalk.dim(" db: .recordings/recordings.db"));
5180
5265
  });
5181
5266
  var appCommand = program.command("app").description("Manage the macOS app installed from this package");
5182
- appCommand.command("install").description("Install a release or explicitly approved local-only Recordings.app artifact").requiredOption("--artifact <path>", "Finalized Recordings.app ZIP artifact").requiredOption("--manifest <path>", "Artifact provenance manifest").option("--envelope <path>", "Signed release envelope (required for release artifacts)").option("--expected-team-id <team>", "Required Developer ID TeamIdentifier for release artifacts").requiredOption("--manifest-sha256 <sha256>", "Authenticated release-manifest SHA-256").requiredOption("--expected-source-sha <sha>", "Exact approved 40-character source commit").requiredOption("--expected-version <version>", "Exact approved release version").option("--expected-hostname <hostname>", "Exact deployment hostname to verify before any install mutation").option("--artifact-policy <policy>", "Artifact policy: release or local-only", "release").option("--approved-target <station>", "Exact approved target; fleet for release artifacts", "fleet").option("--approved-target-identity-kind <kind>", "Target identity kind: hardware_uuid_sha256 or tailscale_node_id_sha256").option("--approved-target-identity-sha256 <sha256>", "Authenticated SHA-256 of the approved target identity; none for release artifacts", "none").option("--acknowledge-local-signing-and-permissions", "Acknowledge local-only ad-hoc identity and possible permission reauthorization").option("--expected-old-identity-sha256 <sha256>", "Exact installed identity approved for migration").option("--expected-new-identity-sha256 <sha256>", "Exact candidate identity approved for migration").option("--allow-signing-identity-migration", "Allow one reviewed signer change that requires new macOS permission approval").option("--allow-adhoc-identity-migration", "Accept that replacing an ad-hoc signed local-only app voids its Microphone and Accessibility grants").option("--launch", "Launch and verify the canonical app after installation").option("--launch-timeout <seconds>", "Canonical process launch timeout").action((opts) => {
5267
+ appCommand.command("install").description("Install a release or explicitly approved local-only HasnaRecordings.app artifact").requiredOption("--artifact <path>", "Finalized HasnaRecordings.app ZIP artifact").requiredOption("--manifest <path>", "Artifact provenance manifest").option("--envelope <path>", "Signed release envelope (required for release artifacts)").option("--expected-team-id <team>", "Required Developer ID TeamIdentifier for release artifacts").requiredOption("--manifest-sha256 <sha256>", "Authenticated release-manifest SHA-256").requiredOption("--expected-source-sha <sha>", "Exact approved 40-character source commit").requiredOption("--expected-version <version>", "Exact approved release version").option("--expected-hostname <hostname>", "Exact deployment hostname to verify before any install mutation").option("--artifact-policy <policy>", "Artifact policy: release or local-only", "release").option("--approved-target <station>", "Exact approved target; fleet for release artifacts", "fleet").option("--variant <variant>", "Artifact variant the operator intends to install: full or bar (default: full)", "full").option("--approved-target-identity-kind <kind>", "Target identity kind: hardware_uuid_sha256 or tailscale_node_id_sha256").option("--approved-target-identity-sha256 <sha256>", "Authenticated SHA-256 of the approved target identity; none for release artifacts", "none").option("--acknowledge-local-signing-and-permissions", "Acknowledge local-only ad-hoc identity and possible permission reauthorization").option("--expected-old-identity-sha256 <sha256>", "Exact installed identity approved for migration").option("--expected-new-identity-sha256 <sha256>", "Exact candidate identity approved for migration").option("--allow-signing-identity-migration", "Allow one reviewed signer change that requires new macOS permission approval").option("--allow-adhoc-identity-migration", "Accept that replacing an ad-hoc signed local-only app voids its Microphone and Accessibility grants").option("--launch", "Launch and verify the canonical app after installation").option("--launch-timeout <seconds>", "Canonical process launch timeout").action((opts) => {
5183
5268
  if (process.platform !== "darwin") {
5184
- console.error(chalk.red("Recordings.app installation is only supported on macOS"));
5269
+ console.error(chalk.red("HasnaRecordings.app installation is only supported on macOS"));
5270
+ process.exit(1);
5271
+ }
5272
+ if (opts.variant !== "full" && opts.variant !== "bar") {
5273
+ console.error(chalk.red("Artifact variant must be full or bar."));
5185
5274
  process.exit(1);
5186
5275
  }
5187
5276
  if (opts.artifactPolicy === "release") {
5277
+ if (opts.variant !== "full") {
5278
+ console.error(chalk.red("Release installs are always full-app artifacts. The bar variant installs " + "through the local-only path (--artifact-policy local-only --variant bar)."));
5279
+ process.exit(1);
5280
+ }
5188
5281
  if (!opts.envelope) {
5189
5282
  console.error(chalk.red("Release installation requires --envelope."));
5190
5283
  process.exit(1);
@@ -5221,10 +5314,14 @@ appCommand.command("install").description("Install a release or explicitly appro
5221
5314
  console.error(chalk.red(error instanceof Error ? error.message : String(error)));
5222
5315
  process.exit(1);
5223
5316
  }
5224
- const updateClientPath = "/Applications/Recordings.app/Contents/Helpers/recordings-update-client";
5317
+ const home = process.env.HOME || process.env.USERPROFILE || "";
5318
+ const canonicalAppPath = pathJoin(home, "Applications", "HasnaRecordings.app");
5319
+ const legacyInstallPaths = findLegacyMacOSAppPaths(home, canonicalAppPath);
5320
+ const installedAppPath = resolveInstalledAppPath(home, canonicalAppPath, legacyInstallPaths);
5321
+ const updateClientPath = pathJoin(installedAppPath, "Contents", "Helpers", "recordings-update-client");
5225
5322
  if (!existsSync8(updateClientPath)) {
5226
5323
  preparedInputs.cleanup();
5227
- console.error(chalk.red("Root-owned Recordings update broker client is not installed."));
5324
+ console.error(chalk.red(`Root-owned Recordings update broker client is not installed at ${updateClientPath}.`));
5228
5325
  process.exit(1);
5229
5326
  }
5230
5327
  const result2 = (() => {
@@ -5302,6 +5399,7 @@ appCommand.command("install").description("Install a release or explicitly appro
5302
5399
  if (opts.expectedHostname) {
5303
5400
  installerArgs.push("--expected-hostname", opts.expectedHostname);
5304
5401
  }
5402
+ installerArgs.push("--variant", opts.variant);
5305
5403
  if (opts.approvedTargetIdentityKind) {
5306
5404
  installerArgs.push("--approved-target-identity-kind", opts.approvedTargetIdentityKind);
5307
5405
  }
@@ -5337,7 +5435,7 @@ appCommand.command("install").description("Install a release or explicitly appro
5337
5435
  }
5338
5436
  process.exit(result.status ?? 1);
5339
5437
  });
5340
- appCommand.command("status").description("Show installed Recordings.app status").option("--verbose", "Show package paths, code hash, and log path").action((opts) => {
5438
+ appCommand.command("status").description("Show installed HasnaRecordings.app status").option("--verbose", "Show package paths, code hash, and log path").action((opts) => {
5341
5439
  const status = getMacOSAppStatus();
5342
5440
  const trigger = probeTriggerDiagnostics({
5343
5441
  accessibilityPermission: process.platform === "darwin" ? status.accessibility_permission : null,
@@ -5347,7 +5445,7 @@ appCommand.command("status").description("Show installed Recordings.app status")
5347
5445
  console.log(JSON.stringify({ ...status, trigger }, null, 2));
5348
5446
  return;
5349
5447
  }
5350
- console.log(chalk.bold("Recordings.app"));
5448
+ console.log(chalk.bold("Hasna Recordings.app"));
5351
5449
  console.log(`Installed: ${status.installed ? "yes" : "no"}`);
5352
5450
  console.log(`Executable: ${status.executable ? "available" : "missing"}`);
5353
5451
  console.log(`Installer: ${status.installer_available ? "available" : "missing"}`);
@@ -5377,7 +5475,7 @@ appCommand.command("status").description("Show installed Recordings.app status")
5377
5475
  console.log(chalk.dim("Use --verbose for paths/code hash/log, or --json for the full status object."));
5378
5476
  }
5379
5477
  });
5380
- appCommand.command("permissions").description("Show macOS permission state for Recordings.app").action(() => {
5478
+ appCommand.command("permissions").description("Show macOS permission state for HasnaRecordings.app").action(() => {
5381
5479
  const status = getMacOSAppStatus();
5382
5480
  const permissions = {
5383
5481
  platform: status.platform,
@@ -5413,21 +5511,21 @@ appCommand.command("permissions").description("Show macOS permission state for R
5413
5511
  }
5414
5512
  console.log(`Log: ${permissions.log_path}`);
5415
5513
  });
5416
- appCommand.command("reset-permissions").description("Reset macOS Microphone and Accessibility permissions for Recordings.app").action(() => {
5514
+ appCommand.command("reset-permissions").description("Reset macOS Microphone and Accessibility permissions for HasnaRecordings.app").action(() => {
5417
5515
  if (process.platform !== "darwin") {
5418
5516
  console.error(chalk.red("Permission reset is only available on macOS"));
5419
5517
  process.exit(1);
5420
5518
  }
5421
5519
  resetMacOSPermissions();
5422
5520
  });
5423
- appCommand.command("request-permissions").description("Open Recordings.app and trigger macOS Microphone and Accessibility permission prompts").option("--reset", "Reset existing Microphone and Accessibility decisions before requesting").action((opts) => {
5521
+ appCommand.command("request-permissions").description("Open HasnaRecordings.app and trigger macOS Microphone and Accessibility permission prompts").option("--reset", "Reset existing Microphone and Accessibility decisions before requesting").action((opts) => {
5424
5522
  if (process.platform !== "darwin") {
5425
5523
  console.error(chalk.red("Permission prompts are only available on macOS"));
5426
5524
  process.exit(1);
5427
5525
  }
5428
5526
  const status = getMacOSAppStatus();
5429
5527
  if (!status.installed) {
5430
- console.error(chalk.red("Recordings.app is not installed. Run: recordings app install"));
5528
+ console.error(chalk.red("HasnaRecordings.app is not installed. Run: recordings app install"));
5431
5529
  process.exit(1);
5432
5530
  }
5433
5531
  if (opts.reset) {
@@ -5439,7 +5537,7 @@ appCommand.command("request-permissions").description("Open Recordings.app and t
5439
5537
  }
5440
5538
  process.exit(result.exitCode);
5441
5539
  });
5442
- appCommand.command("log").description("Show the Recordings.app diagnostic log").option("-n, --lines <lines>", "Number of lines to print", String(DEFAULT_LOG_LINES)).action((opts) => {
5540
+ appCommand.command("log").description("Show the HasnaRecordings.app diagnostic log").option("-n, --lines <lines>", "Number of lines to print", String(DEFAULT_LOG_LINES)).action((opts) => {
5443
5541
  const status = getMacOSAppStatus();
5444
5542
  if (!existsSync8(status.log_path)) {
5445
5543
  console.log("");
@@ -5465,14 +5563,14 @@ appCommand.command("snapshot [output]").description("Write the current main desk
5465
5563
  console.log(chalk.green(`Desktop snapshot written: ${path}`));
5466
5564
  }
5467
5565
  });
5468
- appCommand.command("open").description("Open the installed Recordings.app").action(() => {
5566
+ appCommand.command("open").description("Open the installed HasnaRecordings.app").action(() => {
5469
5567
  const status = getMacOSAppStatus();
5470
5568
  if (process.platform !== "darwin") {
5471
- console.error(chalk.red("Recordings.app can only be opened on macOS"));
5569
+ console.error(chalk.red("HasnaRecordings.app can only be opened on macOS"));
5472
5570
  process.exit(1);
5473
5571
  }
5474
5572
  if (!status.installed) {
5475
- console.error(chalk.red("Recordings.app is not installed. Run: recordings app install"));
5573
+ console.error(chalk.red("HasnaRecordings.app is not installed. Run: recordings app install"));
5476
5574
  process.exit(1);
5477
5575
  }
5478
5576
  const result = spawnSync7("open", [status.installed_app_path], { stdio: "inherit" });
@@ -5604,7 +5702,7 @@ program.command("check").description("Check system dependencies (sox, API keys)
5604
5702
  console.log(chalk.dim(" Could not read the TCC database. This is NOT a denial and NOT proof the app never " + "asked. Reading it needs Full Disk Access, which is held by the session's " + "RESPONSIBLE process and inherited by its children \u2014 not granted per-tool: on " + "a fleet Mac `bun` is explicitly denied and still reads the database over SSH, " + "because it inherits sshd's grant. So re-run from a plain ssh shell rather than " + "granting Full Disk Access to tmux or bun, and not under sudo, which changes the " + "responsible process. A missing sqlite3, a locked database or a corrupt file " + "produce this same state, so the cause is not established either."));
5605
5703
  }
5606
5704
  if (!micOk && !micUnreadable) {
5607
- console.log(chalk.dim(" Recordings.app cannot capture audio without this. macOS does not error when it is " + "missing \u2014 it delivers silent audio. This grant CANNOT be set remotely; it needs a " + "human at the keyboard:"));
5705
+ console.log(chalk.dim(" HasnaRecordings.app cannot capture audio without this. macOS does not error when it is " + "missing \u2014 it delivers silent audio. This grant CANNOT be set remotely; it needs a " + "human at the keyboard:"));
5608
5706
  const instruction = microphoneGrantInstruction({
5609
5707
  installedAppPath: macStatus.installed_app_path,
5610
5708
  otherAppPaths: macStatus.legacy_install_paths,
@@ -5763,7 +5861,7 @@ Bye.`));
5763
5861
  }
5764
5862
  });
5765
5863
  });
5766
- program.command("shortcut").description("Show or change the global recording trigger (macOS). Exits non-zero when a change was " + "written while Recordings.app was running, because the running instance keeps the " + "trigger it registered with \u2014 the write is stored but not armed until it is reopened.").option("--set <chord>", 'Set the app hotkey, e.g. "f13" or "ctrl+opt+r"').option("--reset", "Reset the app hotkey to the app's built-in default").option("--fn <state>", "Use fn/Globe as push-to-talk: on|off").option("--keys", "List the key names accepted by --set").option("--script", "Write the app-less toggle script and print its path").option("--raycast", "Generate a Raycast script command (requires Raycast)").option("--karabiner", "Generate a Karabiner-Elements rule (requires Karabiner-Elements)").option("--skhd", "Print an skhd hotkey config (requires skhd)").option("--hammerspoon", "Print a Hammerspoon config (requires Hammerspoon)").action((opts) => {
5864
+ program.command("shortcut").description("Show or change the global recording trigger (macOS). Exits non-zero when a change was " + "written while HasnaRecordings.app was running, because the running instance keeps the " + "trigger it registered with \u2014 the write is stored but not armed until it is reopened.").option("--set <chord>", 'Set the app hotkey, e.g. "f13" or "ctrl+opt+r"').option("--reset", "Reset the app hotkey to the app's built-in default").option("--fn <state>", "Use fn/Globe as push-to-talk: on|off").option("--keys", "List the key names accepted by --set").option("--script", "Write the app-less toggle script and print its path").option("--raycast", "Generate a Raycast script command (requires Raycast)").option("--karabiner", "Generate a Karabiner-Elements rule (requires Karabiner-Elements)").option("--skhd", "Print an skhd hotkey config (requires skhd)").option("--hammerspoon", "Print a Hammerspoon config (requires Hammerspoon)").action((opts) => {
5767
5865
  const { writeFileSync: writeFileSync4, mkdirSync: mkdirSync5, chmodSync: chmodSync4, existsSync: existsSync9 } = __require("fs");
5768
5866
  const { join: pathJoin2 } = __require("path");
5769
5867
  const { homedir: getHome } = __require("os");
@@ -5877,10 +5975,10 @@ program.command("shortcut").description("Show or change the global recording tri
5877
5975
  function reportPickup() {
5878
5976
  const pickup = describeTriggerPickup(runningBundles());
5879
5977
  if (pickup.armed) {
5880
- console.log(chalk.dim(" Recordings.app is not running; it will register this on next launch."));
5978
+ console.log(chalk.dim(" HasnaRecordings.app is not running; it will register this on next launch."));
5881
5979
  return;
5882
5980
  }
5883
- console.log(chalk.yellow(" Recordings.app is running and still holds the previous trigger.") + `
5981
+ console.log(chalk.yellow(" HasnaRecordings.app is running and still holds the previous trigger.") + `
5884
5982
  Quit and reopen it to arm this one:`);
5885
5983
  for (const path of pickup.runningBundlePaths)
5886
5984
  console.log(chalk.dim(` ${path}`));
@@ -5939,7 +6037,7 @@ program.command("shortcut").description("Show or change the global recording tri
5939
6037
  ` + ` ${fnGrant.settingsPath} > enable Recordings`);
5940
6038
  const target = grantTargetPaths();
5941
6039
  if (target.paths.length === 0) {
5942
- console.log(chalk.dim(" (no installed Recordings.app found to grant it to)"));
6040
+ console.log(chalk.dim(" (no installed HasnaRecordings.app found to grant it to)"));
5943
6041
  }
5944
6042
  for (const path of target.paths) {
5945
6043
  console.log(chalk.dim(` grant it to: ${path}${target.running ? "" : " (not running \u2014 installed copy)"}`));
@@ -5966,7 +6064,7 @@ program.command("shortcut").description("Show or change the global recording tri
5966
6064
  # Toggle recording on/off. Run this from a global hotkey.
5967
6065
  # Each press toggles: start recording -> stop + transcribe + copy to clipboard
5968
6066
  #
5969
- # This is the app-less path. If Recordings.app is running, prefer its own hotkey
6067
+ # This is the app-less path. If HasnaRecordings.app is running, prefer its own hotkey
5970
6068
  # ("recordings shortcut --set ..."): the app streams transcription and pastes into
5971
6069
  # the focused field, which this script cannot do.
5972
6070
  set -e
@@ -6037,7 +6135,7 @@ fi
6037
6135
  title: "Recordings \u2014 Fn key to toggle recording",
6038
6136
  rules: [
6039
6137
  {
6040
- description: "Fn key toggles speech recording (open-recordings)",
6138
+ description: "Fn key toggles speech recording (recordings)",
6041
6139
  manipulators: [
6042
6140
  {
6043
6141
  type: "basic",
@@ -6467,7 +6565,7 @@ program.command("feedback <message>").description("Send feedback").option("--ema
6467
6565
  function getMacOSAppStatus() {
6468
6566
  const packageRoot = findPackageRoot();
6469
6567
  const home = process.env.HOME || process.env.USERPROFILE || "";
6470
- const canonicalAppPath = pathJoin(home, "Applications", "Recordings.app");
6568
+ const canonicalAppPath = pathJoin(home, "Applications", "HasnaRecordings.app");
6471
6569
  const legacyInstallPaths = findLegacyMacOSAppPaths(home, canonicalAppPath);
6472
6570
  const installedAppPath = resolveInstalledAppPath(home, canonicalAppPath, legacyInstallPaths);
6473
6571
  const installedAppPathForGrants = existsSync8(installedAppPath) ? installedAppPath : null;
@@ -6515,7 +6613,7 @@ function buildPermissionWarnings(status) {
6515
6613
  warnings.push(`no app bundle exists at ${status.installed_app_path}, so the states above describe no ` + "installed code \u2014 install the app before trusting them");
6516
6614
  }
6517
6615
  if (status.ambiguous_installations) {
6518
- warnings.push("more than one Recordings.app is installed, so the states above may describe a bundle " + `other than the one macOS granted: reporting on ${status.installed_app_path}, also ` + `present are ${status.legacy_install_paths.join(", ")}`);
6616
+ warnings.push("more than one HasnaRecordings.app is installed, so the states above may describe a bundle " + `other than the one macOS granted: reporting on ${status.installed_app_path}, also ` + `present are ${status.legacy_install_paths.join(", ")}`);
6519
6617
  }
6520
6618
  for (const [service, durability] of [
6521
6619
  ["Microphone", status.microphone_grant_durability],
@@ -6538,7 +6636,9 @@ function buildPermissionWarnings(status) {
6538
6636
  function resolveInstalledAppPath(home, canonicalPath, legacyPaths) {
6539
6637
  const preference = [
6540
6638
  canonicalPath,
6639
+ pathJoin("/", "Applications", "HasnaRecordings.app"),
6541
6640
  pathJoin("/", "Applications", "Recordings.app"),
6641
+ pathJoin(home, ".hasna", "recordings", "HasnaRecordings.app"),
6542
6642
  pathJoin(home, ".hasna", "recordings", "Recordings.app")
6543
6643
  ];
6544
6644
  for (const candidate of preference) {
@@ -6550,12 +6650,14 @@ function resolveInstalledAppPath(home, canonicalPath, legacyPaths) {
6550
6650
  function findLegacyMacOSAppPaths(home, canonicalPath) {
6551
6651
  const candidates = [
6552
6652
  pathJoin(home, ".hasna", "recordings", "Recordings.app"),
6553
- pathJoin("/", "Applications", "Recordings.app")
6653
+ pathJoin(home, ".hasna", "recordings", "HasnaRecordings.app"),
6654
+ pathJoin("/", "Applications", "Recordings.app"),
6655
+ pathJoin("/", "Applications", "HasnaRecordings.app")
6554
6656
  ];
6555
6657
  const userApplications = pathJoin(home, "Applications");
6556
6658
  if (existsSync8(userApplications)) {
6557
6659
  for (const entry of readdirSync2(userApplications, { withFileTypes: true })) {
6558
- if (entry.isDirectory() && entry.name.startsWith("Recordings.app.")) {
6660
+ if (entry.isDirectory() && (entry.name.startsWith("Recordings.app.") || entry.name.startsWith("HasnaRecordings.app."))) {
6559
6661
  candidates.push(pathJoin(userApplications, entry.name));
6560
6662
  }
6561
6663
  }
@@ -80,7 +80,7 @@ export declare const TOGGLE_RECORDING_DEFAULTS_KEY = "KeyboardShortcuts_toggleRe
80
80
  * thing — and so the claim can be pinned against the Swift source that implements it.
81
81
  *
82
82
  * Verified against the shipped bundle on 2026-07-27 with `nm -u` on
83
- * Recordings.app/Contents/MacOS/Recordings, which lists `_RegisterEventHotKey` (the
83
+ * HasnaRecordings.app/Contents/MacOS/Recordings, which lists `_RegisterEventHotKey` (the
84
84
  * hotkey) and `_CGEventTapCreate` + `_AXIsProcessTrusted` (the fn monitor).
85
85
  */
86
86
  export interface TriggerGrantRequirement {
@@ -124,7 +124,7 @@ export interface BundleScanProbes {
124
124
  readBundleIdentifier?: BundleIdentifierReader;
125
125
  }
126
126
  /**
127
- * Bundle paths of the Recordings.app instances currently running.
127
+ * Bundle paths of the HasnaRecordings.app instances currently running.
128
128
  *
129
129
  * TCC grants key to a specific bundle (and, for an unstably-signed build, to its cdhash),
130
130
  * so "which bundle is running" is the only honest answer to "where does the Accessibility
@@ -1,5 +1,5 @@
1
1
  /**
2
- * PostgreSQL migrations for open-recordings storage sync.
2
+ * PostgreSQL migrations for recordings storage sync.
3
3
  *
4
4
  * Equivalent to the SQLite schema in database.ts, translated for PostgreSQL.
5
5
  */
@@ -1,7 +1,6 @@
1
1
  export type Env = Record<string, string | undefined>;
2
2
  /** Where a client reads and writes. Two arms, no third. */
3
3
  export type ClientStore = "sqlite" | "http";
4
- export declare function defaultApiBaseUrl(name: string): string;
5
4
  export declare function toV1BaseUrl(apiUrl: string): string;
6
5
  export type TransportKind = ClientStore;
7
6
  export interface TransportResolution {
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/http/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/http/client.ts"],"names":[],"mappings":"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"}