ReviewOS

also looking at this

stacks/launchpad

Claude/app issues review e7f207

#16
Merged glennmichael123 wants to merge claude/app-issues-review-e7f207 into main
8 files +112 -35
resources/functions/applescript.tsmodified+24-2
Changes to resources/functions/applescript.ts
@@ -18,9 +18,26 @@ export const cleanField = (value: string | undefined) => {
1818 return text === 'missing value' ? '' : text
1919}
2020
21export interface ScriptOutcome { ok: boolean, result?: string, error?: { code?: number, message?: string } }
21/**
22 * `error` is what a host that reports AppleScript error numbers returns. `reason` is what the Craft runtime returns
23 * when the bridge itself could not start, such as `NSAppleScript unavailable`: no Apple event is ever sent, so nothing
24 * is refused and nothing appears in the Automation pane. Craft's own `AppleScriptResult` declares neither.
25 */
26export interface ScriptOutcome { ok: boolean, result?: string, reason?: string, error?: { code?: number, message?: string } }
2227export interface ScriptFailure { message: string, permission?: 'accessibility' | 'automation' }
2328
29/** A failure the person can act on, carrying the privacy pane that settles it when there is one. */
30export class PermissionError extends Error {
31 constructor(message: string, readonly permission?: 'accessibility' | 'automation') { super(message) }
32}
33
34/**
35 * The Automation pane has no way to add an app: a row for Launchpad appears only once macOS has asked, and each app
36 * Launchpad controls is a separate switch underneath it. So the advice names both the prompt and the switch, instead of
37 * sending someone to a pane that may still be empty.
38 */
39const automationAdvice = (app: string) => `Launchpad needs your permission to control ${app}. Choose OK when macOS asks, or, if you answered Don't Allow before, switch ${app} on under Launchpad in System Settings > Privacy & Security > Automation.`
40
2441/**
2542 * What to tell someone when a script did not run. Newer Craft hosts report the AppleScript error number, which separates
2643 * a refused permission from a script that broke; older hosts report only that it failed, so the permission hint stays the
@@ -28,12 +45,17 @@ export interface ScriptFailure { message: string, permission?: 'accessibility' |
2845 */
2946export function describeScriptFailure(app: string, outcome: ScriptOutcome): ScriptFailure {
3047 const code = typeof outcome.error?.code === 'number' ? outcome.error.code : undefined
31 const automation: ScriptFailure = { message: `Allow Launchpad to control ${app} under System Settings > Privacy & Security > Automation, then try again.`, permission: 'automation' }
48 const automation: ScriptFailure = { message: automationAdvice(app), permission: 'automation' }
49 // A script that never ran was never refused, so sending someone to a privacy pane would only waste their time.
50 const reason = outcome.reason?.trim()
51 if (code === undefined && reason) return { message: `Launchpad could not run that script: ${reason}. The script never reached ${app}, so no permission is missing.` }
3252 if (code === undefined) return automation
3353 if (code === -1743 || code === -1744) return automation
3454 if (code === -25211 || code === -1719) return { message: 'Allow Launchpad under System Settings > Privacy & Security > Accessibility, then try again.', permission: 'accessibility' }
3555 if (code === -128) return { message: 'The request was cancelled.' }
3656 if (code === -600) return { message: `${app} is not running and could not be opened.` }
57 // A dialog of the app's own, such as the Finder warning before the Trash is emptied, holds the event open until it times out.
58 if (code === -1712) return { message: `${app} is waiting for an answer. Switch to ${app}, finish what it is asking, then try again.` }
3759 const detail = outcome.error?.message?.trim()
3860 return { message: detail ? `${app} could not do that: ${detail}` : `${app} could not do that (error ${code}).` }
3961}