@@ -19,6 +19,7 @@ import {
1919 logoutCodexAuth ,
2020 logoutGitAuth ,
2121 logoutGrokAuth ,
22+ readClaudeAuthStatus ,
2223 readCodexAuthStatus ,
2324 readGitAuthStatus ,
2425 readGrokAuthStatus ,
@@ -514,6 +515,108 @@ describe("api auth", () => {
514515 } )
515516 ) . pipe ( Effect . provide ( NodeContext . layer ) ) )
516517
518+ it . effect ( "reads labeled Claude OAuth status from controller state" , ( ) =>
519+ withTempDir ( ( root ) =>
520+ Effect . gen ( function * ( _ ) {
521+ const fs = yield * _ ( FileSystem . FileSystem )
522+ const path = yield * _ ( Path . Path )
523+ const projectsRoot = path . join ( root , ".docker-git" )
524+ const accountDir = path . join ( projectsRoot , ".orch" , "auth" , "claude" , "team-a" )
525+
526+ yield * _ ( fs . makeDirectory ( accountDir , { recursive : true } ) )
527+ yield * _ ( fs . writeFileString ( path . join ( accountDir , ".oauth-token" ) , "sk-ant-oat01-test\n" ) )
528+
529+ const status = yield * _ (
530+ withProjectsRoot (
531+ projectsRoot ,
532+ withWorkingDirectory ( root , readClaudeAuthStatus ( "team-a" ) )
533+ )
534+ )
535+
536+ expect ( status . connected ) . toBe ( true )
537+ expect ( status . label ) . toBe ( "team-a" )
538+ expect ( status . method ) . toBe ( "oauth-token" )
539+ expect ( status . authPath ) . toBe ( accountDir )
540+ expect ( status . message ) . toBe ( "Claude connected (team-a, oauth-token)." )
541+ expect ( JSON . stringify ( status ) ) . not . toContain ( "sk-ant-oat01-test" )
542+ } )
543+ ) . pipe ( Effect . provide ( NodeContext . layer ) ) )
544+
545+ it . effect ( "reads labeled Claude root session credentials from controller state" , ( ) =>
546+ withTempDir ( ( root ) =>
547+ Effect . gen ( function * ( _ ) {
548+ const fs = yield * _ ( FileSystem . FileSystem )
549+ const path = yield * _ ( Path . Path )
550+ const projectsRoot = path . join ( root , ".docker-git" )
551+ const accountDir = path . join ( projectsRoot , ".orch" , "auth" , "claude" , "team-a" )
552+
553+ yield * _ ( fs . makeDirectory ( accountDir , { recursive : true } ) )
554+ yield * _ ( fs . writeFileString ( path . join ( accountDir , ".credentials.json" ) , "{\"session\":\"ok\"}\n" ) )
555+
556+ const status = yield * _ (
557+ withProjectsRoot (
558+ projectsRoot ,
559+ withWorkingDirectory ( root , readClaudeAuthStatus ( "team-a" ) )
560+ )
561+ )
562+
563+ expect ( status . connected ) . toBe ( true )
564+ expect ( status . label ) . toBe ( "team-a" )
565+ expect ( status . method ) . toBe ( "claude-ai-session" )
566+ expect ( status . authPath ) . toBe ( accountDir )
567+ expect ( status . message ) . toBe ( "Claude connected (team-a, claude-ai-session)." )
568+ } )
569+ ) . pipe ( Effect . provide ( NodeContext . layer ) ) )
570+
571+ it . effect ( "reads labeled Claude nested session credentials from controller state" , ( ) =>
572+ withTempDir ( ( root ) =>
573+ Effect . gen ( function * ( _ ) {
574+ const fs = yield * _ ( FileSystem . FileSystem )
575+ const path = yield * _ ( Path . Path )
576+ const projectsRoot = path . join ( root , ".docker-git" )
577+ const accountDir = path . join ( projectsRoot , ".orch" , "auth" , "claude" , "team-a" )
578+ const nestedDir = path . join ( accountDir , ".claude" )
579+
580+ yield * _ ( fs . makeDirectory ( nestedDir , { recursive : true } ) )
581+ yield * _ ( fs . writeFileString ( path . join ( nestedDir , ".credentials.json" ) , "{\"session\":\"ok\"}\n" ) )
582+
583+ const status = yield * _ (
584+ withProjectsRoot (
585+ projectsRoot ,
586+ withWorkingDirectory ( root , readClaudeAuthStatus ( "team-a" ) )
587+ )
588+ )
589+
590+ expect ( status . connected ) . toBe ( true )
591+ expect ( status . label ) . toBe ( "team-a" )
592+ expect ( status . method ) . toBe ( "claude-ai-session" )
593+ expect ( status . authPath ) . toBe ( accountDir )
594+ expect ( status . message ) . toBe ( "Claude connected (team-a, claude-ai-session)." )
595+ } )
596+ ) . pipe ( Effect . provide ( NodeContext . layer ) ) )
597+
598+ it . effect ( "reports missing default Claude auth from controller state" , ( ) =>
599+ withTempDir ( ( root ) =>
600+ Effect . gen ( function * ( _ ) {
601+ const path = yield * _ ( Path . Path )
602+ const projectsRoot = path . join ( root , ".docker-git" )
603+ const accountDir = path . join ( projectsRoot , ".orch" , "auth" , "claude" , "default" )
604+
605+ const status = yield * _ (
606+ withProjectsRoot (
607+ projectsRoot ,
608+ withWorkingDirectory ( root , readClaudeAuthStatus ( null ) )
609+ )
610+ )
611+
612+ expect ( status . connected ) . toBe ( false )
613+ expect ( status . label ) . toBe ( "default" )
614+ expect ( status . method ) . toBe ( "none" )
615+ expect ( status . authPath ) . toBe ( accountDir )
616+ expect ( status . message ) . toBe ( "Claude not connected (default)." )
617+ } )
618+ ) . pipe ( Effect . provide ( NodeContext . layer ) ) )
619+
517620 it . effect ( "removes labeled Grok auth from controller state" , ( ) =>
518621 withTempDir ( ( root ) =>
519622 Effect . gen ( function * ( _ ) {
0 commit comments