From d756e57f242c598e8494626d546234b7fc3d1f37 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:21:00 +0100 Subject: [PATCH 01/11] feat: tcc.db interaction updates for macOS 27 & drop macOS 14 in CI --- .github/workflows/test.yml | 42 +++++++---- src/commands/setup/macOS/setup.ts | 4 +- src/commands/setup/macOS/updateTccDb.ts | 92 +++++++++++++++++++++++-- 3 files changed, 117 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d8a932..2cbc926 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,11 +16,11 @@ jobs: matrix: os: [ - macos-14, macos-15, macos-15-intel, macos-26, macos-26-intel, + xcode-27, windows-2022, windows-2025, windows-11-arm, @@ -92,11 +92,11 @@ jobs: matrix: os: [ - macos-14, macos-15, macos-15-intel, macos-26, macos-26-intel, + xcode-27, windows-2022, windows-2025, windows-11-arm, @@ -159,25 +159,32 @@ jobs: "defaultDownload": true, "assets": [ { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "23", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-14.dmg", - "sha256": "c6595f5ca50440e8553cde278936a46eff58d4c904e19c87e7d0e25950617ee1" + "sha256": "fd88500b740bb3389ec295465d35a5351d49a57fe120983aed3c761cfb349c6a" }, { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "24", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-15.dmg", - "sha256": "8bdc3a11c45a19cc876a859bfbd64529469a8b7d4ad41fd7e00a0729ebdbcb25" + "sha256": "ef5a533e38f37aff44682b125b07855b4cd0eb24d04416d48df5097386580799" }, { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "25", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-26.dmg", - "sha256": "9af2a3af7c9bffae1b2af26f1970548ecd62f33965fd30f4e43f21b9f57ce5ca" + "sha256": "c46e353d4f2d4a717d3362211de638ee55fcb58dbec9cf9abd0170c35d41b225" + }, + { + "version": "0.1.0-VoiceOver4", + "platformVersion": "27", + "repository": "guidepup/voiceover", + "asset": "guidepup-voiceover-preferences-xcode-27.dmg", + "sha256": "3ea13925dc75bf42df0ffe981814efc2a6d822b13321e2d5b0cb77593d15cee2" } ] } @@ -248,25 +255,32 @@ jobs: "defaultDownload": true, "assets": [ { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "23", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-14.dmg", - "sha256": "c6595f5ca50440e8553cde278936a46eff58d4c904e19c87e7d0e25950617ee1" + "sha256": "fd88500b740bb3389ec295465d35a5351d49a57fe120983aed3c761cfb349c6a" }, { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "24", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-15.dmg", - "sha256": "8bdc3a11c45a19cc876a859bfbd64529469a8b7d4ad41fd7e00a0729ebdbcb25" + "sha256": "ef5a533e38f37aff44682b125b07855b4cd0eb24d04416d48df5097386580799" }, { - "version": "0.0.1", + "version": "0.0.1-VoiceOver4", "platformVersion": "25", "repository": "guidepup/voiceover", "asset": "guidepup-voiceover-preferences-macos-26.dmg", - "sha256": "9af2a3af7c9bffae1b2af26f1970548ecd62f33965fd30f4e43f21b9f57ce5ca" + "sha256": "c46e353d4f2d4a717d3362211de638ee55fcb58dbec9cf9abd0170c35d41b225" + }, + { + "version": "0.1.0-VoiceOver4", + "platformVersion": "27", + "repository": "guidepup/voiceover", + "asset": "guidepup-voiceover-preferences-xcode-27.dmg", + "sha256": "3ea13925dc75bf42df0ffe981814efc2a6d822b13321e2d5b0cb77593d15cee2" } ] } diff --git a/src/commands/setup/macOS/setup.ts b/src/commands/setup/macOS/setup.ts index 1fea7cc..07570a3 100644 --- a/src/commands/setup/macOS/setup.ts +++ b/src/commands/setup/macOS/setup.ts @@ -5,7 +5,7 @@ import { disableSplashScreenSystemDefaults } from "./disableSplashScreenSystemDe import { disableDictationInputAutoEnable } from "./disableDictationInputAutoEnable"; import { isSipEnabled } from "./isSipEnabled"; import { writeDatabaseFile } from "./writeDatabaseFile"; -import { SYSTEM_PATH, USER_PATH, updateTccDb } from "./updateTccDb"; +import { getUserTccDbPath, SYSTEM_PATH, updateTccDb } from "./updateTccDb"; import { isAppleScriptControlEnabled } from "./isAppleScriptControlEnabled"; import { handleNote, handleWarning } from "../../../logging"; import { ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../../../errors"; @@ -27,7 +27,7 @@ export async function setup({ }: MacOSSetupOptions = {}): Promise { if (!macosIgnoreTccDb) { try { - await updateTccDb(USER_PATH); + await updateTccDb(getUserTccDbPath()); } catch (e) { if (ci) { throw e; diff --git a/src/commands/setup/macOS/updateTccDb.ts b/src/commands/setup/macOS/updateTccDb.ts index 4c29ab8..f114825 100644 --- a/src/commands/setup/macOS/updateTccDb.ts +++ b/src/commands/setup/macOS/updateTccDb.ts @@ -189,12 +189,92 @@ const getEntries = (): string[] => { const TIMEOUT_BACKOFFS = [1000, 1000, 3000, 5000, 8000]; -export const USER_PATH = `${homedir()}/Library/Application Support/com.apple.TCC/TCC.db`; export const SYSTEM_PATH = "/Library/Application Support/com.apple.TCC/TCC.db"; +const LEGACY_USER_PATH = `${homedir()}/Library/Application Support/com.apple.TCC/TCC.db`; +const PROTECTED_SYSTEM_PATH = "/private/var/containers/Data/ProtectedSystem"; + +function getMacOsMajorVersion(): number { + const major = parseInt(release().split(".")[0], 10); + + if (Number.isNaN(major)) { + throw new Error(`Unexpected macOS version: ${release()}`); + } + + return major; +} + +function execFileSyncAsRoot( + file: string, + args: string[], + options?: Parameters[2], +): string { + return execFileSync("sudo", [file, ...args], { + ...options, + encoding: "utf8", + }) as string; +} + +export function getUserTccDbPath(): string { + const macOsMajor = getMacOsMajorVersion(); + + if (macOsMajor < 27) { + return LEGACY_USER_PATH; + } + + const databases = execFileSyncAsRoot("find", [ + PROTECTED_SYSTEM_PATH, + "-mindepth", + "6", + "-maxdepth", + "6", + "-type", + "f", + "-path", + "*/Data/Library/Application Support/com.apple.TCC/TCC.db", + "-print", + ]) + .trim() + .split("\n") + .filter(Boolean); + + if (databases.length === 0) { + throw new Error("Unable to find a ProtectedSystem TCC database"); + } + + if (databases.length === 1) { + return databases[0]; + } + + const openFiles = execFileSyncAsRoot("lsof", ["-c", "tccd", "-Fn"]) + .split("\n") + .filter((line) => line.startsWith("n")) + .map((line) => line.slice(1)) + .filter( + (path) => + path.startsWith(`${PROTECTED_SYSTEM_PATH}/`) && + /\/com\.apple\.TCC\/TCC\.db$/.test(path), + ); + + const activeDatabases = databases.filter((database) => + openFiles.includes(database), + ); + + if (activeDatabases.length !== 1) { + throw new Error( + [ + "Unable to identify one active ProtectedSystem TCC database:", + ...databases.map((database) => ` ${database}`), + ].join("\n"), + ); + } + + return activeDatabases[0]; +} + export async function updateTccDb(path: string): Promise { - const osRelease = release(); - const isSonomaOrNewer = parseInt(osRelease.split(".")[0], 10) >= 23; + const macOsMajor = getMacOsMajorVersion(); + const isSonomaOrNewer = macOsMajor >= 23; for (const values of getEntries()) { const query = `INSERT OR IGNORE INTO access VALUES(${values}${ @@ -203,10 +283,12 @@ export async function updateTccDb(path: string): Promise { for (let i = 0; i < TIMEOUT_BACKOFFS.length + 1; i++) { try { - execFileSync("sqlite3", [path, query], { + execFileSyncAsRoot("sqlite3", [path, query], { encoding: "utf8", stdio: "ignore", }); + + break; } catch (cause) { if (i === TIMEOUT_BACKOFFS.length) { throw new Error(ERR_SETUP_MACOS_UNABLE_TO_WRITE_USER_TCC_DB, { @@ -221,6 +303,6 @@ export async function updateTccDb(path: string): Promise { } } - // 1s sleep to give cache for updates to propagate + // Give the TCC cache time to observe the database updates. await new Promise((resolve) => setTimeout(resolve, 1000)); } From 3de25a07c543638efe90e6c15e83b3b3a404a44d Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:31:25 +0100 Subject: [PATCH 02/11] feat: drop legacy resolution fix --- .github/workflows/resolutionFix.ts | 12 ------------ package.json | 5 ++--- 2 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 .github/workflows/resolutionFix.ts diff --git a/.github/workflows/resolutionFix.ts b/.github/workflows/resolutionFix.ts deleted file mode 100644 index 0cbbbaa..0000000 --- a/.github/workflows/resolutionFix.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { execSync } from "child_process"; - -if (process.platform === "darwin") { - try { - execSync( - `"/Library/Application Support/VMware Tools/vmware-resolutionSet" 1920 1080`, - { encoding: "utf8" } - ); - } catch (_) { - // swallow - } -} diff --git a/package.json b/package.json index 8e1e803..7e9b2cd 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,11 @@ ], "scripts": { "build": "yarn clean && yarn compile", - "ci": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start:setup --ci --macos-record", - "ci:ignore-tcc-db": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start:setup --ci --macos-record --macos-ignore-tcc-db", + "ci": "yarn clean && yarn lint && yarn build && yarn start:setup --ci --macos-record", + "ci:ignore-tcc-db": "yarn clean && yarn lint && yarn build && yarn start:setup --ci --macos-record --macos-ignore-tcc-db", "clean": "rimraf lib", "compile": "tsc", "dev": "ts-node ./src/index.ts", - "resolutionFix": "ts-node ./.github/workflows/resolutionFix.ts", "lint": "eslint . --ext .ts", "lint:fix": "yarn lint --fix", "start:setup": "node ./bin/guidepup setup", From f8b4342649c29add6073352f47185bc9b275b205 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:32:23 +0100 Subject: [PATCH 03/11] feat: named columns for query --- src/commands/setup/macOS/updateTccDb.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/commands/setup/macOS/updateTccDb.ts b/src/commands/setup/macOS/updateTccDb.ts index f114825..9f3b555 100644 --- a/src/commands/setup/macOS/updateTccDb.ts +++ b/src/commands/setup/macOS/updateTccDb.ts @@ -275,9 +275,27 @@ export function getUserTccDbPath(): string { export async function updateTccDb(path: string): Promise { const macOsMajor = getMacOsMajorVersion(); const isSonomaOrNewer = macOsMajor >= 23; + const columns = [ + "service", + "client", + "client_type", + "auth_value", + "auth_reason", + "auth_version", + "csreq", + "policy_id", + "indirect_object_identifier_type", + "indirect_object_identifier", + "indirect_object_code_identity", + "flags", + "last_modified", + ...(isSonomaOrNewer + ? ["pid", "pid_version", "boot_uuid", "last_reminded"] + : []), + ]; for (const values of getEntries()) { - const query = `INSERT OR IGNORE INTO access VALUES(${values}${ + const query = `INSERT OR IGNORE INTO access (${columns.join(",")}) VALUES(${values}${ isSonomaOrNewer ? `,NULL,NULL,'UNUSED',${epoch}` : "" });`; From 1db5d85b3950e56fcb8efb5c39bb3a3998f4b9a6 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:41:47 +0100 Subject: [PATCH 04/11] tmp: VoiceOverStarter debug --- .../macOS/ensureLocalPreferencesExist.ts | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index c6b1622..61c411e 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -29,11 +29,48 @@ function getPreferencesDirectory(): string { } async function startVoiceOver(): Promise { - execSync( - "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter &", - { stdio: "ignore", timeout: 2000 }, + const voiceOverStarterPath = + "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter"; + let matchingExecutables: string; + + try { + matchingExecutables = execFileSync( + "find", + [ + "/System/Library", + "-type", + "f", + "(", + "-iname", + "*voiceover*", + "-o", + "-name", + "VoiceOverStarter", + ")", + "-print", + ], + { encoding: "utf8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }, + ).trim(); + } catch (cause) { + matchingExecutables = `Search failed: ${String(cause)}`; + } + + console.info( + "VoiceOver launch diagnostics:", + JSON.stringify( + { + darwinVersion: platformMajorVersion(), + legacyStarterPath: voiceOverStarterPath, + legacyStarterExists: existsSync(voiceOverStarterPath), + matchingExecutables, + }, + null, + 2, + ), ); + execSync(`${voiceOverStarterPath} &`, { stdio: "ignore", timeout: 2000 }); + await new Promise((resolve) => setTimeout(resolve, 1000)); } From 663d1e4ba9f3adc711ab0cfd22769504bd26326a Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:44:15 +0100 Subject: [PATCH 05/11] tmp: try and find VoiceOverStarter --- .../macOS/ensureLocalPreferencesExist.ts | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index 61c411e..877335e 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -31,29 +31,22 @@ function getPreferencesDirectory(): string { async function startVoiceOver(): Promise { const voiceOverStarterPath = "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter"; - let matchingExecutables: string; + const voiceOverPath = + "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver"; - try { - matchingExecutables = execFileSync( - "find", - [ - "/System/Library", - "-type", - "f", - "(", - "-iname", - "*voiceover*", - "-o", - "-name", - "VoiceOverStarter", - ")", - "-print", - ], - { encoding: "utf8", timeout: 10000, stdio: ["ignore", "pipe", "ignore"] }, - ).trim(); - } catch (cause) { - matchingExecutables = `Search failed: ${String(cause)}`; - } + const spotlightResults = ["VoiceOverStarter", "VoiceOver"].map((name) => { + try { + const matches = execFileSync("mdfind", ["-name", name], { + encoding: "utf8", + timeout: 5000, + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + + return `${name}: ${matches || "no matches"}`; + } catch (cause) { + return `${name}: search failed: ${String(cause)}`; + } + }); console.info( "VoiceOver launch diagnostics:", @@ -62,7 +55,9 @@ async function startVoiceOver(): Promise { darwinVersion: platformMajorVersion(), legacyStarterPath: voiceOverStarterPath, legacyStarterExists: existsSync(voiceOverStarterPath), - matchingExecutables, + legacyVoiceOverPath: voiceOverPath, + legacyVoiceOverExists: existsSync(voiceOverPath), + spotlightResults, }, null, 2, From 70d0b937d2bdc76eccb658030cf10bccb6783cc6 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:50:42 +0100 Subject: [PATCH 06/11] tmp: debug fs structure inside VoiceOver --- .../macOS/ensureLocalPreferencesExist.ts | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index 877335e..6078843 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -33,20 +33,25 @@ async function startVoiceOver(): Promise { "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter"; const voiceOverPath = "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver"; + const voiceOverContentsPath = + "/System/Library/CoreServices/VoiceOver.app/Contents"; - const spotlightResults = ["VoiceOverStarter", "VoiceOver"].map((name) => { - try { - const matches = execFileSync("mdfind", ["-name", name], { + let voiceOverContentsListing: string; + + try { + voiceOverContentsListing = execFileSync( + "ls", + ["-laR", voiceOverContentsPath], + { encoding: "utf8", - timeout: 5000, + timeout: 10000, + maxBuffer: 5 * 1024 * 1024, stdio: ["ignore", "pipe", "ignore"], - }).trim(); - - return `${name}: ${matches || "no matches"}`; - } catch (cause) { - return `${name}: search failed: ${String(cause)}`; - } - }); + }, + ).trim(); + } catch (cause) { + voiceOverContentsListing = `Listing failed: ${String(cause)}`; + } console.info( "VoiceOver launch diagnostics:", @@ -57,7 +62,7 @@ async function startVoiceOver(): Promise { legacyStarterExists: existsSync(voiceOverStarterPath), legacyVoiceOverPath: voiceOverPath, legacyVoiceOverExists: existsSync(voiceOverPath), - spotlightResults, + voiceOverContentsListing, }, null, 2, From 9048435cdde93554a883da1c454884bbb7bb00f9 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 22:59:27 +0100 Subject: [PATCH 07/11] feat: just open VoiceOver for macOS 27 --- .github/workflows/test.yml | 42 +++++++-------- .../macOS/ensureLocalPreferencesExist.ts | 52 +++++-------------- 2 files changed, 33 insertions(+), 61 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cbc926..592d2b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,18 +14,17 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: - [ - macos-15, - macos-15-intel, - macos-26, - macos-26-intel, + os: [ + # macos-15, + # macos-15-intel, + # macos-26, + # macos-26-intel, xcode-27, - windows-2022, - windows-2025, - windows-11-arm, - ubuntu-26.04, - ubuntu-26.04-arm, + # windows-2022, + # windows-2025, + # windows-11-arm, + # ubuntu-26.04, + # ubuntu-26.04-arm, ] steps: - uses: actions/checkout@v6 @@ -90,18 +89,17 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: - [ - macos-15, - macos-15-intel, - macos-26, - macos-26-intel, + os: [ + # macos-15, + # macos-15-intel, + # macos-26, + # macos-26-intel, xcode-27, - windows-2022, - windows-2025, - windows-11-arm, - ubuntu-26.04, - ubuntu-26.04-arm, + # windows-2022, + # windows-2025, + # windows-11-arm, + # ubuntu-26.04, + # ubuntu-26.04-arm, ] steps: - uses: actions/checkout@v6 diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index 6078843..57fa757 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -28,49 +28,23 @@ function getPreferencesDirectory(): string { return join(homedir(), "Library", "Preferences"); } -async function startVoiceOver(): Promise { - const voiceOverStarterPath = - "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter"; - const voiceOverPath = - "/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOver"; - const voiceOverContentsPath = - "/System/Library/CoreServices/VoiceOver.app/Contents"; +const voiceOverAppPath = "/System/Library/CoreServices/VoiceOver.app"; - let voiceOverContentsListing: string; +const voiceOverStarterPath = `${voiceOverAppPath}/Contents/MacOS/VoiceOverStarter`; - try { - voiceOverContentsListing = execFileSync( - "ls", - ["-laR", voiceOverContentsPath], - { - encoding: "utf8", - timeout: 10000, - maxBuffer: 5 * 1024 * 1024, - stdio: ["ignore", "pipe", "ignore"], - }, - ).trim(); - } catch (cause) { - voiceOverContentsListing = `Listing failed: ${String(cause)}`; +async function startVoiceOver(): Promise { + if (platformMajorVersion() >= 27) { + execFileSync("/usr/bin/open", ["-a", voiceOverAppPath], { + stdio: "ignore", + timeout: 2000, + }); + } else { + execSync(`${voiceOverStarterPath} &`, { + stdio: "ignore", + timeout: 2000, + }); } - console.info( - "VoiceOver launch diagnostics:", - JSON.stringify( - { - darwinVersion: platformMajorVersion(), - legacyStarterPath: voiceOverStarterPath, - legacyStarterExists: existsSync(voiceOverStarterPath), - legacyVoiceOverPath: voiceOverPath, - legacyVoiceOverExists: existsSync(voiceOverPath), - voiceOverContentsListing, - }, - null, - 2, - ), - ); - - execSync(`${voiceOverStarterPath} &`, { stdio: "ignore", timeout: 2000 }); - await new Promise((resolve) => setTimeout(resolve, 1000)); } From c01264b6b114d20095b9b2d12c469db87d339220 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 23:02:50 +0100 Subject: [PATCH 08/11] tmp: debug --- .../macOS/ensureLocalPreferencesExist.ts | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index 57fa757..b1ba4dc 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -32,12 +32,52 @@ const voiceOverAppPath = "/System/Library/CoreServices/VoiceOver.app"; const voiceOverStarterPath = `${voiceOverAppPath}/Contents/MacOS/VoiceOverStarter`; +function logVoiceOverProcesses(phase: string): void { + const captureMatchingLines = (file: string, args: string[]): string => { + try { + const output = execFileSync(file, args, { + encoding: "utf8", + timeout: 2000, + stdio: ["ignore", "pipe", "ignore"], + }); + const matchingLines = output + .split("\n") + .filter((line) => /voiceover/i.test(line)); + + return matchingLines.join("\n") || "No VoiceOver matches"; + } catch (cause) { + return `Query failed: ${String(cause)}`; + } + }; + + console.log( + "VoiceOver process diagnostics:", + JSON.stringify( + { + darwinVersion: platformMajorVersion(), + phase, + processes: captureMatchingLines("ps", [ + "-axo", + "pid=,ppid=,comm=,args=", + ]), + launchdServices: captureMatchingLines("launchctl", ["list"]), + }, + null, + 2, + ), + ); +} + async function startVoiceOver(): Promise { - if (platformMajorVersion() >= 27) { + const darwinMajorVersion = platformMajorVersion(); + + if (darwinMajorVersion >= 27) { + logVoiceOverProcesses("before open"); execFileSync("/usr/bin/open", ["-a", voiceOverAppPath], { stdio: "ignore", timeout: 2000, }); + logVoiceOverProcesses("after open"); } else { execSync(`${voiceOverStarterPath} &`, { stdio: "ignore", @@ -46,6 +86,10 @@ async function startVoiceOver(): Promise { } await new Promise((resolve) => setTimeout(resolve, 1000)); + + if (darwinMajorVersion >= 27) { + logVoiceOverProcesses("after startup wait"); + } } function isRunning(): boolean { From 02303503255cbe545b324cb82d13d5f8893e80f8 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 23:05:45 +0100 Subject: [PATCH 09/11] tmp: remove debug --- .../macOS/ensureLocalPreferencesExist.ts | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts index b1ba4dc..a69817d 100644 --- a/src/commands/setup/macOS/ensureLocalPreferencesExist.ts +++ b/src/commands/setup/macOS/ensureLocalPreferencesExist.ts @@ -32,52 +32,14 @@ const voiceOverAppPath = "/System/Library/CoreServices/VoiceOver.app"; const voiceOverStarterPath = `${voiceOverAppPath}/Contents/MacOS/VoiceOverStarter`; -function logVoiceOverProcesses(phase: string): void { - const captureMatchingLines = (file: string, args: string[]): string => { - try { - const output = execFileSync(file, args, { - encoding: "utf8", - timeout: 2000, - stdio: ["ignore", "pipe", "ignore"], - }); - const matchingLines = output - .split("\n") - .filter((line) => /voiceover/i.test(line)); - - return matchingLines.join("\n") || "No VoiceOver matches"; - } catch (cause) { - return `Query failed: ${String(cause)}`; - } - }; - - console.log( - "VoiceOver process diagnostics:", - JSON.stringify( - { - darwinVersion: platformMajorVersion(), - phase, - processes: captureMatchingLines("ps", [ - "-axo", - "pid=,ppid=,comm=,args=", - ]), - launchdServices: captureMatchingLines("launchctl", ["list"]), - }, - null, - 2, - ), - ); -} - async function startVoiceOver(): Promise { const darwinMajorVersion = platformMajorVersion(); if (darwinMajorVersion >= 27) { - logVoiceOverProcesses("before open"); execFileSync("/usr/bin/open", ["-a", voiceOverAppPath], { stdio: "ignore", timeout: 2000, }); - logVoiceOverProcesses("after open"); } else { execSync(`${voiceOverStarterPath} &`, { stdio: "ignore", @@ -86,10 +48,6 @@ async function startVoiceOver(): Promise { } await new Promise((resolve) => setTimeout(resolve, 1000)); - - if (darwinMajorVersion >= 27) { - logVoiceOverProcesses("after startup wait"); - } } function isRunning(): boolean { From 38269d2d61f432c8339938673a060a4c026bd604 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 23:06:17 +0100 Subject: [PATCH 10/11] ci: add back full matrix --- .github/workflows/test.yml | 42 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 592d2b8..2cbc926 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,17 +14,18 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ - # macos-15, - # macos-15-intel, - # macos-26, - # macos-26-intel, + os: + [ + macos-15, + macos-15-intel, + macos-26, + macos-26-intel, xcode-27, - # windows-2022, - # windows-2025, - # windows-11-arm, - # ubuntu-26.04, - # ubuntu-26.04-arm, + windows-2022, + windows-2025, + windows-11-arm, + ubuntu-26.04, + ubuntu-26.04-arm, ] steps: - uses: actions/checkout@v6 @@ -89,17 +90,18 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ - # macos-15, - # macos-15-intel, - # macos-26, - # macos-26-intel, + os: + [ + macos-15, + macos-15-intel, + macos-26, + macos-26-intel, xcode-27, - # windows-2022, - # windows-2025, - # windows-11-arm, - # ubuntu-26.04, - # ubuntu-26.04-arm, + windows-2022, + windows-2025, + windows-11-arm, + ubuntu-26.04, + ubuntu-26.04-arm, ] steps: - uses: actions/checkout@v6 From 67bdbfcc0c6e231b842ffd053119944497077fb9 Mon Sep 17 00:00:00 2001 From: cmorten Date: Fri, 25 Sep 2026 23:07:24 +0100 Subject: [PATCH 11/11] chore: package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e9b2cd..9adf164 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@guidepup/setup", - "version": "0.27.0", + "version": "0.28.0", "description": "CLI for configuring environments and install screen reader assets for Guidepup.", "main": "lib/index.js", "typings": "lib/index.d.ts",