@@ -10,6 +10,7 @@ import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
1010import { addIntegrationErrorMessage } from "@executor-js/react/lib/integration-add" ;
1111
1212import { addMcpServer , checkCodexPluginAccess , codexPluginsAtom } from "./atoms" ;
13+ import { accessBlocked , accessPending , type CodexAccessState } from "./codex-access-gate" ;
1314import { CODEX_PERMISSIONS } from "../sdk/codex-permissions" ;
1415
1516// ---------------------------------------------------------------------------
@@ -31,7 +32,7 @@ export default function CodexPluginAdd(props: {
3132 const doCheckAccess = useAtomSet ( checkCodexPluginAccess , { mode : "promiseExit" } ) ;
3233
3334 const [ adding , setAdding ] = useState ( false ) ;
34- const [ access , setAccess ] = useState < { status : string ; message ?: string } | null > ( null ) ;
35+ const [ access , setAccess ] = useState < CodexAccessState | null > ( null ) ;
3536 const [ checking , setChecking ] = useState ( false ) ;
3637 const [ error , setError ] = useState < string | null > ( null ) ;
3738
@@ -46,6 +47,17 @@ export default function CodexPluginAdd(props: {
4647 AsyncResult . isSuccess ( integrationsResult ) &&
4748 integrationsResult . value . some ( ( integration ) => String ( integration . slug ) === plugin . slug ) ;
4849
50+ // Adding is held back until the probe says the plugin can actually run: an
51+ // integration added while macOS is blocking it looks connected and fails on
52+ // its first real call, by which point the person has left the one card that
53+ // explains the fix.
54+ const blocked = accessBlocked ( access ) ;
55+ const pending = accessPending ( {
56+ checking,
57+ declaresPermissions : permissions . length > 0 ,
58+ access,
59+ } ) ;
60+
4961 const handleAdd = async ( ) => {
5062 if ( plugin === undefined ) return ;
5163 setAdding ( true ) ;
@@ -101,7 +113,7 @@ export default function CodexPluginAdd(props: {
101113 const exit = await doCheckAccess ( { params : { id : props . presetId } , reactivityKeys : [ ] } ) ;
102114 setAccess (
103115 Exit . isSuccess ( exit )
104- ? ( exit . value as { status : string ; message ?: string } )
116+ ? ( exit . value as CodexAccessState )
105117 : { status : "blocked" , message : "Could not reach the plugin." } ,
106118 ) ;
107119 setChecking ( false ) ;
@@ -200,8 +212,9 @@ export default function CodexPluginAdd(props: {
200212 macOS access
201213 </ span >
202214 < p className = "text-[12px] text-muted-foreground" >
203- macOS asks the first time this runs. If you miss the prompt, it will not ask again —
204- enable it here.
215+ { blocked
216+ ? "This has to be on before the plugin can be added — turn it on below, then check again."
217+ : "macOS asks the first time this runs. If you miss the prompt, it will not ask again — enable it here." }
205218 </ p >
206219 < div className = "flex items-center gap-3" >
207220 < span
@@ -243,6 +256,19 @@ export default function CodexPluginAdd(props: {
243256 </ div >
244257 ) }
245258
259+ { /* A block on a plugin that declares no macOS access has no panel above
260+ to carry it, so state the reason next to the action it is holding. */ }
261+ { blocked && permissions . length === 0 && (
262+ < div className = "flex items-center gap-3" >
263+ < p className = "text-[12px] text-destructive" >
264+ { access ?. message ?? "This plugin cannot run yet." }
265+ </ p >
266+ < Button type = "button" variant = "secondary" onClick = { ( ) => void handleCheck ( ) } >
267+ Check again
268+ </ Button >
269+ </ div >
270+ ) }
271+
246272 { error !== null && < p className = "text-[12px] text-destructive" > { error } </ p > }
247273
248274 < FloatActions >
@@ -254,8 +280,13 @@ export default function CodexPluginAdd(props: {
254280 View integration
255281 </ Button >
256282 ) : plugin . available ? (
257- < Button type = "button" onClick = { ( ) => void handleAdd ( ) } loading = { adding } >
258- Add integration
283+ < Button
284+ type = "button"
285+ onClick = { ( ) => void handleAdd ( ) }
286+ loading = { adding }
287+ disabled = { blocked || pending }
288+ >
289+ { pending ? "Checking access…" : "Add integration" }
259290 </ Button >
260291 ) : (
261292 < Button asChild >
0 commit comments