@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.
- package/README.md +38 -34
- package/bun.lock +96 -36
- package/dist/__tests__/helpers/installer-guard-execution.d.ts.map +1 -1
- package/dist/__tests__/helpers/source-assertions.d.ts +6 -4
- package/dist/__tests__/helpers/source-assertions.d.ts.map +1 -1
- package/dist/cli/index.js +197 -95
- package/dist/cli/macos-shortcut.d.ts +2 -2
- package/dist/db/pg-migrations.d.ts +1 -1
- package/dist/http/client.d.ts +0 -1
- package/dist/http/client.d.ts.map +1 -1
- package/dist/index.js +162 -73
- package/dist/lib/capture-probe.d.ts +3 -3
- package/dist/lib/capture-probe.d.ts.map +1 -1
- package/dist/lib/macos-bundle.d.ts +1 -1
- package/dist/lib/release-install-policy.d.ts +21 -0
- package/dist/lib/release-install-policy.d.ts.map +1 -1
- package/dist/mcp/index.js +144 -59
- package/dist/sdk/index.d.ts +2 -2
- package/dist/sdk/index.js +7 -3
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/cloud-config.d.ts +4 -19
- package/dist/server/cloud-config.d.ts.map +1 -1
- package/dist/server/cloud.d.ts +1 -1
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +168 -110
- package/dist/server/serve.d.ts.map +1 -1
- package/dist/storage.d.ts +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +144 -60
- package/dist/store.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/package.json +4 -3
- package/packaging/macos/build_release_pkg.sh +10 -4
- package/packaging/macos/managed_bootstrap.sh +4 -4
- package/packaging/macos/scripts/postinstall +2 -2
- package/packaging/macos/scripts/preinstall +2 -0
- package/scripts/generate-sdk.ts +17 -16
- package/scripts/install_macos_app.sh +75 -28
- package/scripts/macos_artifact.ts +162 -45
- package/scripts/migrate.ts +2 -2
- package/scripts/release-suite-gate.ts +181 -0
- package/scripts/set-version.ts +5 -5
- package/scripts/smoke_macos_app.sh +80 -34
- package/scripts/vacuity-manifests/version-sites.tsv +8 -4
- package/src/native/Recordings/App/MenuBarStatusView.swift +9 -4
- package/src/native/Recordings/App/RecordingsApp.swift +37 -1
- package/src/native/Recordings/RecordingsLib/Info.plist +3 -3
- package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +21 -1
- package/src/native/Recordings/RecordingsTests/BarOnlyLaunchPlanTests.swift +99 -0
- package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +2 -2
- package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +1 -1
- package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +1 -1
- package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +2 -2
- package/src/native/Recordings/Updater/Broker/BrokerMain.swift +1 -1
- package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +1 -1
- package/src/native/Recordings/Updater/Broker/InstallJournal.swift +1 -1
- package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +2 -2
- package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +1 -1
- package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +2 -2
- package/src/native/Recordings/build.sh +152 -18
- package/dist/lib/retired-deployment-modes.d.ts +0 -24
- 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 =
|
|
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
|
|
1965
|
-
|
|
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
|
|
1968
|
-
const normalized =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 " + "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
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
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
|
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("
|
|
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("
|
|
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
|
|
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
|
|
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 (
|
|
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", "
|
|
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
|
|
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("
|
|
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
|
-
*
|
|
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
|
|
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
|
package/dist/http/client.d.ts
CHANGED
|
@@ -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":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/http/client.ts"],"names":[],"mappings":"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"}
|