@@ -50,6 +50,8 @@ func pickFromConfigs(configs []types.BrowserConfig, opts PickOptions) ([]Browser
5050 // it's harmless (DPAPI reads Local State per-profile, D-Bus is stateless).
5151 retriever := keyretriever .DefaultRetriever (opts .KeychainPassword )
5252
53+ configs = resolveGlobs (configs )
54+
5355 var browsers []Browser
5456 for _ , cfg := range configs {
5557 if name != "all" && cfg .Key != name {
@@ -94,6 +96,34 @@ type retrieverSetter interface {
9496 SetRetriever (keyretriever.KeyRetriever )
9597}
9698
99+ // resolveGlobs expands glob patterns in browser configs' UserDataDir.
100+ // This supports MSIX/UWP browsers on Windows whose package directories
101+ // contain a dynamic publisher hash suffix (e.g., "TheBrowserCompany.Arc_*").
102+ //
103+ // For literal paths (no glob metacharacters), Glob returns the path itself
104+ // when it exists, so the config passes through unchanged. When a path does
105+ // not exist and contains no metacharacters, Glob returns nil and the
106+ // original config is preserved — the main loop handles "not found" as usual.
107+ //
108+ // When a glob matches multiple directories, the config is duplicated so
109+ // each resolved path is treated as a separate browser data directory.
110+ func resolveGlobs (configs []types.BrowserConfig ) []types.BrowserConfig {
111+ var out []types.BrowserConfig
112+ for _ , cfg := range configs {
113+ matches , _ := filepath .Glob (cfg .UserDataDir )
114+ if len (matches ) == 0 {
115+ out = append (out , cfg )
116+ continue
117+ }
118+ for _ , dir := range matches {
119+ c := cfg
120+ c .UserDataDir = dir
121+ out = append (out , c )
122+ }
123+ }
124+ return out
125+ }
126+
97127// newBrowsers dispatches to the correct engine based on BrowserKind
98128// and converts engine-specific types to the Browser interface.
99129func newBrowsers (cfg types.BrowserConfig ) ([]Browser , error ) {
0 commit comments