@@ -8,8 +8,10 @@ import (
88 "fmt"
99
1010 "github.com/Infisical/infisical-merge/packages/api"
11+ "github.com/Infisical/infisical-merge/packages/config"
1112 "github.com/Infisical/infisical-merge/packages/models"
1213 "github.com/Infisical/infisical-merge/packages/util"
14+ "github.com/go-resty/resty/v2"
1315 "github.com/manifoldco/promptui"
1416 "github.com/posthog/posthog-go"
1517 "github.com/rs/zerolog/log"
@@ -55,29 +57,12 @@ var initCmd = &cobra.Command{
5557 }
5658 httpClient .SetAuthToken (userCreds .UserCredentials .JTWToken )
5759
58- organizationResponse , err := api . CallGetAllOrganizations (httpClient )
60+ selectedOrgID , err := pickOrganization (httpClient , "Which Infisical organization would you like to select a project from?" )
5961 if err != nil {
60- util .HandleError (err , "Unable to pull organizations that belong to you" )
61- }
62-
63- organizations := organizationResponse .Organizations
64-
65- organizationNames := util .GetOrganizationsNameList (organizationResponse )
66-
67- prompt := promptui.Select {
68- Label : "Which Infisical organization would you like to select a project from?" ,
69- Items : organizationNames ,
70- Size : 7 ,
71- }
72-
73- index , _ , err := prompt .Run ()
74- if err != nil {
75- util .HandleError (err )
62+ util .HandleError (err , "Unable to select organization" )
7663 }
7764
78- selectedOrganization := organizations [index ]
79-
80- tokenResponse , err := api .CallSelectOrganization (httpClient , api.SelectOrganizationRequest {OrganizationId : selectedOrganization .ID })
65+ tokenResponse , err := api .CallSelectOrganization (httpClient , api.SelectOrganizationRequest {OrganizationId : selectedOrgID })
8166 if tokenResponse .MfaEnabled {
8267 i := 1
8368 for i < 6 {
@@ -113,7 +98,7 @@ var initCmd = &cobra.Command{
11398 i ++
11499 } else {
115100 httpClient .SetAuthToken (verifyMFAresponse .Token )
116- tokenResponse , err = api .CallSelectOrganization (httpClient , api.SelectOrganizationRequest {OrganizationId : selectedOrganization . ID })
101+ tokenResponse , err = api .CallSelectOrganization (httpClient , api.SelectOrganizationRequest {OrganizationId : selectedOrgID })
117102 break
118103 }
119104 }
@@ -137,15 +122,15 @@ var initCmd = &cobra.Command{
137122 util .HandleError (err , "Unable to pull projects that belong to you" )
138123 }
139124
140- filteredWorkspaces , workspaceNames := util .GetWorkspacesInOrganization (workspaceResponse , selectedOrganization . ID )
125+ filteredWorkspaces , workspaceNames := util .GetWorkspacesInOrganization (workspaceResponse , selectedOrgID )
141126
142- prompt = promptui.Select {
127+ prompt : = promptui.Select {
143128 Label : "Which of your Infisical projects would you like to connect this project to?" ,
144129 Items : workspaceNames ,
145130 Size : 7 ,
146131 }
147132
148- index , _ , err = prompt .Run ()
133+ index , _ , err : = prompt .Run ()
149134 if err != nil {
150135 util .HandleError (err )
151136 }
@@ -164,6 +149,63 @@ func init() {
164149 RootCmd .AddCommand (initCmd )
165150}
166151
152+ // pickOrganization prompts the user to select an organization (and optionally a sub-org).
153+ // GET /v1/organization is always used as the source of truth for the org list.
154+ // GET /v1/organization/accessible-with-sub-orgs is used only to enrich entries with sub-org
155+ // counts and the second-level picker — if it fails or omits an org, that org still appears.
156+ func pickOrganization (httpClient * resty.Client , label string ) (string , error ) {
157+ orgResp , err := api .CallGetAllOrganizations (httpClient )
158+ if err != nil {
159+ return "" , err
160+ }
161+ orgs := orgResp .Organizations
162+ if len (orgs ) == 0 {
163+ util .PrintErrorMessageAndExit (fmt .Sprintf ("You don't have any organization created in Infisical. You must first create a organization at %s" , config .INFISICAL_URL ))
164+ }
165+
166+ // Best-effort: enrich with sub-org data. Ignore any error — the flat list is enough.
167+ subOrgMap := map [string ][]api.SubOrganization {}
168+ if subOrgsResp , err := api .CallGetAllOrganizationsWithSubOrgs (httpClient ); err != nil {
169+ log .Debug ().Err (err ).Msg ("Failed to fetch sub-org data; falling back to flat org list" )
170+ } else {
171+ for _ , o := range subOrgsResp .Organizations {
172+ subOrgMap [o .ID ] = o .SubOrganizations
173+ }
174+ }
175+
176+ labels := util .BuildOrgRootLabels (orgs , subOrgMap )
177+
178+ prompt1 := promptui.Select {
179+ Label : label ,
180+ Items : labels ,
181+ Size : 7 ,
182+ }
183+ index , _ , err := prompt1 .Run ()
184+ if err != nil {
185+ return "" , err
186+ }
187+
188+ selectedOrg := orgs [index ]
189+ subs := subOrgMap [selectedOrg .ID ]
190+
191+ if len (subs ) == 0 {
192+ return selectedOrg .ID , nil
193+ }
194+
195+ // Second prompt: root org itself or one of its sub-orgs
196+ subItems , subLabels := util .BuildSubOrgPickerItems (selectedOrg .ID , selectedOrg .Name , subs )
197+ prompt2 := promptui.Select {
198+ Label : fmt .Sprintf ("Which scope within %s?" , selectedOrg .Name ),
199+ Items : subLabels ,
200+ Size : 7 ,
201+ }
202+ subIndex , _ , err := prompt2 .Run ()
203+ if err != nil {
204+ return "" , err
205+ }
206+ return subItems [subIndex ].ID , nil
207+ }
208+
167209func writeWorkspaceFile (selectedWorkspace models.Workspace ) error {
168210 workspaceFileToSave := models.WorkspaceConfigFile {
169211 WorkspaceId : selectedWorkspace .ID ,
0 commit comments