Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@guidepup/setup",
"version": "0.26.0",
"version": "0.27.0",
"description": "CLI for configuring environments and install screen reader assets for Guidepup.",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
17 changes: 9 additions & 8 deletions src/commands/setup/macOS/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import { isSipEnabled } from "./isSipEnabled";
import { writeDatabaseFile } from "./writeDatabaseFile";
import { SYSTEM_PATH, USER_PATH, updateTccDb } from "./updateTccDb";
import { isAppleScriptControlEnabled } from "./isAppleScriptControlEnabled";
import {
handleNote,
handleSetupManualRequired,
handleWarning,
} from "../../../logging";
import { handleNote, handleWarning } from "../../../logging";
import { ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../../../errors";
import { enableDoNotDisturb } from "./enableDoNotDisturb";
import { enabledDbFile } from "./isAppleScriptControlEnabled/enabledDbFile";
import { ensureLocalPreferencesExist } from "./ensureLocalPreferencesExist";
import { waitForAppleScriptControl } from "./waitForAppleScriptControl";

interface MacOSSetupOptions {
ci?: boolean;
Expand All @@ -37,7 +34,7 @@ export async function setup({
} else {
handleNote(
"Unable to configure automation permissions",
"This can be expected when running the Guidepup setup locally if macOS does not grant the required permissions automatically.\nPlease accept any system dialogs requesting automation permissions while using Guidepup.\nIf you are running Guidepup in CI, use the `--ci` option to skip interactive permission setup.\nAlternatively, please refer to https://www.guidepup.dev/docs/guides/manual-voiceover-setup for instructions on manually configuring VoiceOver permissions.",
"This can be expected when running the Guidepup setup locally if macOS does not grant the required permissions automatically.\n\nPlease accept any system dialogs requesting automation permissions while using Guidepup.\n\nIf you are running Guidepup in CI, use the `--ci` option to skip interactive permission setup.\n\nAlternatively, please refer to https://www.guidepup.dev/docs/guides/manual-voiceover-setup for instructions on manually configuring VoiceOver permissions.",
);
}
}
Expand Down Expand Up @@ -87,7 +84,11 @@ export async function setup({
await enableDoNotDisturb();
}

if (!isSipEnabled() && !(await enabledDbFile())) {
if (await enabledDbFile()) {
return;
}

if (!isSipEnabled()) {
writeDatabaseFile();

return;
Expand All @@ -101,7 +102,7 @@ export async function setup({
throw new Error(ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
}

handleSetupManualRequired();
await waitForAppleScriptControl();
} finally {
stopRecording();
}
Expand Down
44 changes: 44 additions & 0 deletions src/commands/setup/macOS/waitForAppleScriptControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import chalk from "chalk";
import {
ERR_SETUP_MACOS_APPLESCRIPT_CONTROL_ENABLE_TIMEOUT,
ERR_SETUP_MACOS_UNABLE_TO_OPEN_VOICEOVER_UTILITY,
} from "../../../errors";
import { handleNote, handleSetupManualRequired } from "../../../logging";
import { enabledDbFile } from "./isAppleScriptControlEnabled/enabledDbFile";

const open = promisify(execFile);
const ENABLE_TIMEOUT_MS = 5 * 60 * 1000;
const POLL_INTERVAL_MS = 1000;

export async function waitForAppleScriptControl(): Promise<void> {
try {
await open("/usr/bin/open", ["-a", "VoiceOver Utility"]);
} catch (cause) {
handleSetupManualRequired();
throw new Error(ERR_SETUP_MACOS_UNABLE_TO_OPEN_VOICEOVER_UTILITY, {
cause,
});
}

handleNote(
"Interaction required",
`Opening ${chalk.bold("VoiceOver Utility")} for a manual user step.\n\nIn the ${chalk.bold("General")} tab, please tick the checkbox for ${chalk.bold("Allow VoiceOver to be controlled with AppleScript")}.\n\nWaiting for user to apply the change...`,
);

const deadline = Date.now() + ENABLE_TIMEOUT_MS;

while (Date.now() < deadline) {
if (await enabledDbFile()) {
return;
}

await new Promise<void>((resolve) => {
setTimeout(resolve, POLL_INTERVAL_MS);
});
}

handleSetupManualRequired();
throw new Error(ERR_SETUP_MACOS_APPLESCRIPT_CONTROL_ENABLE_TIMEOUT);
}
4 changes: 4 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ export const ERR_SETUP_MACOS_UNABLE_TO_WRITE_USER_TCC_DB =
"Unable to write to the user TCC database\n\nEnsure that SIP is disabled for the database, or pass '--macos-ignore-tcc-db' to ignore TCC database updates";
export const ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION =
"Unable to setup environment without manual user interaction\n\nEnsure that SIP is disabled or preconfigure your environment with 'Allow VoiceOver to be controlled with AppleScript' enabled";
export const ERR_SETUP_MACOS_APPLESCRIPT_CONTROL_ENABLE_TIMEOUT =
"Timed out waiting for 'Allow VoiceOver to be controlled with AppleScript' to be enabled";
export const ERR_SETUP_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB =
'Failed to enable "Do not disturb" mode';
export const ERR_SETUP_MACOS_UNABLE_TO_START_VOICEOVER =
"Unable to start VoiceOver";
export const ERR_SETUP_MACOS_UNABLE_TO_OPEN_VOICEOVER_UTILITY =
"Unable to open VoiceOver Utility";
export const ERR_SETUP_MACOS_UNABLE_TO_STOP_VOICEOVER =
"Unable to stop VoiceOver";
export const ERR_SETUP_MACOS_UNABLE_TO_FIND_VOICEOVER_PREFERENCES =
Expand Down
3 changes: 2 additions & 1 deletion src/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export function handleSetupComplete(): never {
}

export function handleSetupManualRequired(): void {
logInfo(
handleNote(
"Manual setup required",
"Please complete remaining setup by following this guide:\n\n--> " +
chalk.underline(
chalk.bold(
Expand Down
Loading