@@ -66,6 +66,7 @@ class CmdAuth extends CmdBase implements UsageAware {
6666 commands. add(new LoginCmd ())
6767 commands. add(new LogoutCmd ())
6868 commands. add(new ConfigCmd ())
69+ commands. add(new StatusCmd ())
6970 }
7071
7172 void usage () {
@@ -1019,4 +1020,172 @@ class CmdAuth extends CmdBase implements UsageAware {
10191020 result << ' '
10201021 }
10211022 }
1023+
1024+ class StatusCmd implements SubCmd {
1025+
1026+ @Override
1027+ String getName () { ' status' }
1028+
1029+ @Override
1030+ void apply (List<String > args ) {
1031+ if (args. size() > 0 ) {
1032+ throw new AbortOperationException (" Too many arguments for status command" )
1033+ }
1034+
1035+ def config = readConfig()
1036+
1037+ println " Nextflow Seqera Platform authentication status"
1038+ println " "
1039+
1040+ // API endpoint
1041+ def endpointInfo = getConfigValue(config, ' tower.endpoint' , ' TOWER_API_ENDPOINT' , ' https://api.cloud.seqera.io' )
1042+ println " API endpoint: ${ endpointInfo.value} (${ endpointInfo.source} )"
1043+
1044+ // Access token status
1045+ def tokenInfo = getConfigValue(config, ' tower.accessToken' , ' TOWER_ACCESS_TOKEN' )
1046+ if (tokenInfo. value) {
1047+ println " Access token: yes (${ tokenInfo.source} )"
1048+ } else {
1049+ println " Access token: no"
1050+ }
1051+
1052+ // Monitoring enabled
1053+ def enabledInfo = getConfigValue(config, ' tower.enabled' , null , ' false' )
1054+ def enabledValue = enabledInfo. value?. toString()?. toLowerCase() in [' true' , ' 1' , ' yes' ] ? ' Yes' : ' No'
1055+ println " Local workflow monitoring enabled: ${ enabledValue} (${ enabledInfo.source ?: 'default'} )"
1056+
1057+ // Default workspace
1058+ def workspaceInfo = getConfigValue(config, ' tower.workspaceId' , ' TOWER_WORKFLOW_ID' )
1059+ if (workspaceInfo. value) {
1060+ // Try to get workspace name from API if we have a token
1061+ def workspaceName = null
1062+ if (tokenInfo. value) {
1063+ workspaceName = getWorkspaceNameFromApi(tokenInfo. value as String , endpointInfo. value as String , workspaceInfo. value as String )
1064+ }
1065+
1066+ if (workspaceName) {
1067+ println " Default workspace: ${ workspaceName} [${ workspaceInfo.value} ] (${ workspaceInfo.source} )"
1068+ } else {
1069+ println " Default workspace: ${ workspaceInfo.value} (${ workspaceInfo.source} )"
1070+ }
1071+ } else {
1072+ println " Default workspace: Personal workspace (default)"
1073+ }
1074+
1075+ println " "
1076+ println " System health status"
1077+
1078+ // API connection check
1079+ def apiConnectionOk = checkApiConnection(endpointInfo. value as String )
1080+ println " API connection: ${ apiConnectionOk ? 'OK' : 'ERROR'} "
1081+
1082+ // Authentication check
1083+ if (tokenInfo. value) {
1084+ try {
1085+ def userInfo = callUserInfoApi(tokenInfo. value as String , endpointInfo. value as String )
1086+ def currentUser = userInfo. userName
1087+ println " Authentication: OK (${ currentUser} )"
1088+ } catch (Exception e) {
1089+ println " Authentication: ERROR"
1090+ }
1091+ } else {
1092+ println " Authentication: ERROR (no token)"
1093+ }
1094+ }
1095+
1096+ private String shortenPath (String path ) {
1097+ def userHome = System . getProperty(' user.home' )
1098+ if (path. startsWith(userHome)) {
1099+ return ' ~' + path. substring(userHome. length())
1100+ }
1101+ return path
1102+ }
1103+
1104+ private Map getConfigValue (Map config , String configKey , String envVarName , String defaultValue = null ) {
1105+ def configValue = config[configKey]
1106+ def envValue = System . getenv(envVarName)
1107+ def effectiveValue = configValue ?: envValue ?: defaultValue
1108+
1109+ def source = null
1110+ if (configValue) {
1111+ source = shortenPath(getConfigFile(). toString())
1112+ } else if (envValue) {
1113+ source = " env var \$ ${ envVarName} "
1114+ } else if (defaultValue) {
1115+ source = " default"
1116+ }
1117+
1118+ return [
1119+ value : effectiveValue,
1120+ source : source,
1121+ fromConfig : configValue != null ,
1122+ fromEnv : envValue != null ,
1123+ isDefault : ! configValue && ! envValue
1124+ ]
1125+ }
1126+
1127+ private String getWorkspaceNameFromApi (String accessToken , String endpoint , String workspaceId ) {
1128+ try {
1129+ // Get user info to get user ID
1130+ def userInfo = callUserInfoApi(accessToken, endpoint)
1131+ def userId = userInfo. id as String
1132+
1133+ // Get workspaces for the user
1134+ def workspacesUrl = " ${ endpoint} /user/${ userId} /workspaces"
1135+ def connection = new URL (workspacesUrl). openConnection() as HttpURLConnection
1136+ connection. requestMethod = ' GET'
1137+ connection. connectTimeout = 10000 // 10 second timeout
1138+ connection. readTimeout = 10000
1139+ connection. setRequestProperty(' Authorization' , " Bearer ${ accessToken} " )
1140+
1141+ if (connection. responseCode != 200 ) {
1142+ return null
1143+ }
1144+
1145+ def response = connection. inputStream. text
1146+ def json = new groovy.json.JsonSlurper (). parseText(response) as Map
1147+ def orgsAndWorkspaces = json. orgsAndWorkspaces as List
1148+
1149+ // Find the workspace with matching ID
1150+ def workspace = orgsAndWorkspaces. find { ((Map )it). workspaceId?. toString() == workspaceId }
1151+ if (workspace) {
1152+ def ws = workspace as Map
1153+ return " ${ ws.orgName} / ${ ws.workspaceFullName} "
1154+ }
1155+
1156+ return null
1157+ } catch (Exception e) {
1158+ return null
1159+ }
1160+ }
1161+
1162+ private boolean checkApiConnection (String endpoint ) {
1163+ try {
1164+ def serviceInfoUrl = " ${ endpoint} /service-info"
1165+ def connection = new URL (serviceInfoUrl). openConnection() as HttpURLConnection
1166+ connection. requestMethod = ' GET'
1167+ connection. connectTimeout = 10000 // 10 second timeout
1168+ connection. readTimeout = 10000
1169+
1170+ return connection. responseCode == 200
1171+ } catch (Exception e) {
1172+ return false
1173+ }
1174+ }
1175+
1176+
1177+ @Override
1178+ void usage (List<String > result ) {
1179+ result << ' Show authentication status and configuration'
1180+ result << " Usage: nextflow auth $name " . toString()
1181+ result << ' '
1182+ result << ' This command shows:'
1183+ result << ' - Authentication status (yes/no) and source'
1184+ result << ' - API endpoint and source'
1185+ result << ' - Monitoring enabled status and source'
1186+ result << ' - Default workspace and source'
1187+ result << ' - System health status (API connection and authentication)'
1188+ result << ' '
1189+ }
1190+ }
10221191}
0 commit comments