@@ -20,24 +20,27 @@ import type {
2020 AgentsData ,
2121 ConfigSaveResult ,
2222 ConfigIssue ,
23+ ConfigOptionsData ,
24+ WorkspaceScope ,
2325} from "../types" ;
2426
2527export interface APIClient {
26- getStatus ( projectPath ?: string , signal ?: AbortSignal ) : Promise < WorkspaceStatus > ;
27- getConfig ( projectPath ?: string , signal ?: AbortSignal ) : Promise < ConfigData > ;
28- getDoctor ( projectPath ?: string , signal ?: AbortSignal ) : Promise < DoctorReport > ;
28+ getStatus ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < WorkspaceStatus > ;
29+ getConfig ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < ConfigData > ;
30+ getConfigs ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < ConfigOptionsData > ;
31+ getDoctor ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < DoctorReport > ;
2932 probeProject ( projectPath : string , signal ?: AbortSignal ) : Promise < ProjectProbe > ;
30- getOpeners ( projectPath ?: string , signal ?: AbortSignal ) : Promise < OpenersData > ;
31- getAgents ( projectPath ?: string , signal ?: AbortSignal ) : Promise < AgentsData > ;
32- runAction ( action : WorkspaceAction , target ?: string , projectPath ?: string ) : Promise < ActionResult > ;
33+ getOpeners ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < OpenersData > ;
34+ getAgents ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < AgentsData > ;
35+ runAction ( action : WorkspaceAction , target ?: string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > ;
3336 runWorkspaceAction ( request : WorkspaceActionRequest ) : Promise < ActionResult > ;
34- stopSlot ( sessionName : string , projectPath ?: string ) : Promise < ActionResult > ;
37+ stopSlot ( sessionName : string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > ;
3538 getApiInfo ( signal ?: AbortSignal ) : Promise < { port : number ; config_path : string ; state_path : string } > ;
3639 getProfiles ( signal ?: AbortSignal ) : Promise < Profile [ ] > ;
37- initWorkspace ( profile : string , bootstrapSessions : boolean , projectPath ?: string ) : Promise < InitResult > ;
40+ initWorkspace ( profile : string , bootstrapSessions : boolean , scope ?: WorkspaceScope | string ) : Promise < InitResult > ;
3841 saveConfig (
3942 content : string ,
40- projectPath ?: string ,
43+ scope ?: WorkspaceScope | string ,
4144 baseMtime ?: number | null ,
4245 baseContentHash ?: string | null
4346 ) : Promise < ConfigSaveResult > ;
@@ -59,8 +62,19 @@ export class APIRequestError extends Error {
5962 }
6063}
6164
62- function qs ( projectPath ?: string ) : string {
63- return projectPath ? `?project_path=${ encodeURIComponent ( projectPath ) } ` : "" ;
65+ function normalizeScope ( scope ?: WorkspaceScope | string ) : WorkspaceScope {
66+ if ( ! scope ) return { } ;
67+ if ( typeof scope === "string" ) return { projectPath : scope } ;
68+ return scope ;
69+ }
70+
71+ function qs ( scope ?: WorkspaceScope | string ) : string {
72+ const { projectPath, configPath } = normalizeScope ( scope ) ;
73+ const params = new URLSearchParams ( ) ;
74+ if ( projectPath ) params . set ( "project_path" , projectPath ) ;
75+ if ( configPath ) params . set ( "config_path" , configPath ) ;
76+ const query = params . toString ( ) ;
77+ return query ? `?${ query } ` : "" ;
6478}
6579
6680/**
@@ -73,22 +87,29 @@ export class HTTPClient implements APIClient {
7387 this . baseUrl = baseUrl . replace ( / \/ $ / , "" ) ;
7488 }
7589
76- async getStatus ( projectPath ?: string , signal ?: AbortSignal ) : Promise < WorkspaceStatus > {
77- const res = await fetch ( `${ this . baseUrl } /api/status${ qs ( projectPath ) } ` , { signal } ) ;
90+ async getStatus ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < WorkspaceStatus > {
91+ const res = await fetch ( `${ this . baseUrl } /api/status${ qs ( scope ) } ` , { signal } ) ;
7892 const data = await res . json ( ) ;
7993 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
8094 return data as WorkspaceStatus ;
8195 }
8296
83- async getConfig ( projectPath ?: string , signal ?: AbortSignal ) : Promise < ConfigData > {
84- const res = await fetch ( `${ this . baseUrl } /api/config${ qs ( projectPath ) } ` , { signal } ) ;
97+ async getConfig ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < ConfigData > {
98+ const res = await fetch ( `${ this . baseUrl } /api/config${ qs ( scope ) } ` , { signal } ) ;
8599 const data = await res . json ( ) ;
86100 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
87101 return data as ConfigData ;
88102 }
89103
90- async getDoctor ( projectPath ?: string , signal ?: AbortSignal ) : Promise < DoctorReport > {
91- const res = await fetch ( `${ this . baseUrl } /api/doctor${ qs ( projectPath ) } ` , { signal } ) ;
104+ async getConfigs ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < ConfigOptionsData > {
105+ const res = await fetch ( `${ this . baseUrl } /api/configs${ qs ( scope ) } ` , { signal } ) ;
106+ const data = await res . json ( ) ;
107+ if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
108+ return data as ConfigOptionsData ;
109+ }
110+
111+ async getDoctor ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < DoctorReport > {
112+ const res = await fetch ( `${ this . baseUrl } /api/doctor${ qs ( scope ) } ` , { signal } ) ;
92113 const data = await res . json ( ) ;
93114 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
94115 return data as DoctorReport ;
@@ -101,27 +122,27 @@ export class HTTPClient implements APIClient {
101122 return data as ProjectProbe ;
102123 }
103124
104- async getOpeners ( projectPath ?: string , signal ?: AbortSignal ) : Promise < OpenersData > {
105- const res = await fetch ( `${ this . baseUrl } /api/openers${ qs ( projectPath ) } ` , { signal } ) ;
125+ async getOpeners ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < OpenersData > {
126+ const res = await fetch ( `${ this . baseUrl } /api/openers${ qs ( scope ) } ` , { signal } ) ;
106127 const data = await res . json ( ) ;
107128 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
108129 return data as OpenersData ;
109130 }
110131
111- async getAgents ( projectPath ?: string , signal ?: AbortSignal ) : Promise < AgentsData > {
112- const res = await fetch ( `${ this . baseUrl } /api/agents${ qs ( projectPath ) } ` , { signal } ) ;
132+ async getAgents ( scope ?: WorkspaceScope | string , signal ?: AbortSignal ) : Promise < AgentsData > {
133+ const res = await fetch ( `${ this . baseUrl } /api/agents${ qs ( scope ) } ` , { signal } ) ;
113134 const data = await res . json ( ) ;
114135 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
115136 return data as AgentsData ;
116137 }
117138
118- async runAction ( action : WorkspaceAction , target ?: string , projectPath ?: string ) : Promise < ActionResult > {
119- return this . runWorkspaceAction ( { action, target, projectPath } ) ;
139+ async runAction ( action : WorkspaceAction , target ?: string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > {
140+ return this . runWorkspaceAction ( { action, target, ... normalizeScope ( scope ) } ) ;
120141 }
121142
122143 async runWorkspaceAction ( request : WorkspaceActionRequest ) : Promise < ActionResult > {
123- const { projectPath, stopRemoved, ...body } = request ;
124- const res = await fetch ( `${ this . baseUrl } /api/action${ qs ( projectPath ) } ` , {
144+ const { projectPath, configPath , stopRemoved, ...body } = request ;
145+ const res = await fetch ( `${ this . baseUrl } /api/action${ qs ( { projectPath, configPath } ) } ` , {
125146 method : "POST" ,
126147 headers : { "Content-Type" : "application/json" } ,
127148 body : JSON . stringify ( { ...body , stop_removed : stopRemoved } ) ,
@@ -131,8 +152,8 @@ export class HTTPClient implements APIClient {
131152 return data as ActionResult ;
132153 }
133154
134- async stopSlot ( sessionName : string , projectPath ?: string ) : Promise < ActionResult > {
135- return this . runAction ( "stop" , sessionName , projectPath ) ;
155+ async stopSlot ( sessionName : string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > {
156+ return this . runAction ( "stop" , sessionName , scope ) ;
136157 }
137158
138159 async getApiInfo ( signal ?: AbortSignal ) : Promise < { port : number ; config_path : string ; state_path : string } > {
@@ -149,8 +170,8 @@ export class HTTPClient implements APIClient {
149170 return data . profiles as Profile [ ] ;
150171 }
151172
152- async initWorkspace ( profile : string , bootstrapSessions : boolean , projectPath ?: string ) : Promise < InitResult > {
153- const res = await fetch ( `${ this . baseUrl } /api/init${ qs ( projectPath ) } ` , {
173+ async initWorkspace ( profile : string , bootstrapSessions : boolean , scope ?: WorkspaceScope | string ) : Promise < InitResult > {
174+ const res = await fetch ( `${ this . baseUrl } /api/init${ qs ( scope ) } ` , {
154175 method : "POST" ,
155176 headers : { "Content-Type" : "application/json" } ,
156177 body : JSON . stringify ( { profile, bootstrap_sessions : bootstrapSessions } ) ,
@@ -162,11 +183,11 @@ export class HTTPClient implements APIClient {
162183
163184 async saveConfig (
164185 content : string ,
165- projectPath ?: string ,
186+ scope ?: WorkspaceScope | string ,
166187 baseMtime ?: number | null ,
167188 baseContentHash ?: string | null
168189 ) : Promise < ConfigSaveResult > {
169- const res = await fetch ( `${ this . baseUrl } /api/config${ qs ( projectPath ) } ` , {
190+ const res = await fetch ( `${ this . baseUrl } /api/config${ qs ( scope ) } ` , {
170191 method : "POST" ,
171192 headers : { "Content-Type" : "application/json" } ,
172193 body : JSON . stringify ( {
@@ -195,25 +216,33 @@ export class TauriClient implements APIClient {
195216 return `http://127.0.0.1:${ info . port } ` ;
196217 }
197218
198- async getStatus ( projectPath ?: string ) : Promise < WorkspaceStatus > {
219+ async getStatus ( scope ?: WorkspaceScope | string ) : Promise < WorkspaceStatus > {
199220 const baseUrl = await this . _baseUrl ( ) ;
200- const res = await fetch ( `${ baseUrl } /api/status${ qs ( projectPath ) } ` ) ;
221+ const res = await fetch ( `${ baseUrl } /api/status${ qs ( scope ) } ` ) ;
201222 const data = await res . json ( ) ;
202223 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
203224 return data as WorkspaceStatus ;
204225 }
205226
206- async getConfig ( projectPath ?: string ) : Promise < ConfigData > {
227+ async getConfig ( scope ?: WorkspaceScope | string ) : Promise < ConfigData > {
207228 const baseUrl = await this . _baseUrl ( ) ;
208- const res = await fetch ( `${ baseUrl } /api/config${ qs ( projectPath ) } ` ) ;
229+ const res = await fetch ( `${ baseUrl } /api/config${ qs ( scope ) } ` ) ;
209230 const data = await res . json ( ) ;
210231 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
211232 return data as ConfigData ;
212233 }
213234
214- async getDoctor ( projectPath ?: string ) : Promise < DoctorReport > {
235+ async getConfigs ( scope ?: WorkspaceScope | string ) : Promise < ConfigOptionsData > {
236+ const baseUrl = await this . _baseUrl ( ) ;
237+ const res = await fetch ( `${ baseUrl } /api/configs${ qs ( scope ) } ` ) ;
238+ const data = await res . json ( ) ;
239+ if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
240+ return data as ConfigOptionsData ;
241+ }
242+
243+ async getDoctor ( scope ?: WorkspaceScope | string ) : Promise < DoctorReport > {
215244 const baseUrl = await this . _baseUrl ( ) ;
216- const res = await fetch ( `${ baseUrl } /api/doctor${ qs ( projectPath ) } ` ) ;
245+ const res = await fetch ( `${ baseUrl } /api/doctor${ qs ( scope ) } ` ) ;
217246 const data = await res . json ( ) ;
218247 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
219248 return data as DoctorReport ;
@@ -227,30 +256,30 @@ export class TauriClient implements APIClient {
227256 return data as ProjectProbe ;
228257 }
229258
230- async getOpeners ( projectPath ?: string ) : Promise < OpenersData > {
259+ async getOpeners ( scope ?: WorkspaceScope | string ) : Promise < OpenersData > {
231260 const baseUrl = await this . _baseUrl ( ) ;
232- const res = await fetch ( `${ baseUrl } /api/openers${ qs ( projectPath ) } ` ) ;
261+ const res = await fetch ( `${ baseUrl } /api/openers${ qs ( scope ) } ` ) ;
233262 const data = await res . json ( ) ;
234263 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
235264 return data as OpenersData ;
236265 }
237266
238- async getAgents ( projectPath ?: string ) : Promise < AgentsData > {
267+ async getAgents ( scope ?: WorkspaceScope | string ) : Promise < AgentsData > {
239268 const baseUrl = await this . _baseUrl ( ) ;
240- const res = await fetch ( `${ baseUrl } /api/agents${ qs ( projectPath ) } ` ) ;
269+ const res = await fetch ( `${ baseUrl } /api/agents${ qs ( scope ) } ` ) ;
241270 const data = await res . json ( ) ;
242271 if ( ! res . ok ) throw new Error ( data . error || `HTTP ${ res . status } ` ) ;
243272 return data as AgentsData ;
244273 }
245274
246- async runAction ( action : WorkspaceAction , target ?: string , projectPath ?: string ) : Promise < ActionResult > {
247- return this . runWorkspaceAction ( { action, target, projectPath } ) ;
275+ async runAction ( action : WorkspaceAction , target ?: string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > {
276+ return this . runWorkspaceAction ( { action, target, ... normalizeScope ( scope ) } ) ;
248277 }
249278
250279 async runWorkspaceAction ( request : WorkspaceActionRequest ) : Promise < ActionResult > {
251- const { projectPath, stopRemoved, ...body } = request ;
280+ const { projectPath, configPath , stopRemoved, ...body } = request ;
252281 const baseUrl = await this . _baseUrl ( ) ;
253- const res = await fetch ( `${ baseUrl } /api/action${ qs ( projectPath ) } ` , {
282+ const res = await fetch ( `${ baseUrl } /api/action${ qs ( { projectPath, configPath } ) } ` , {
254283 method : "POST" ,
255284 headers : { "Content-Type" : "application/json" } ,
256285 body : JSON . stringify ( { ...body , stop_removed : stopRemoved } ) ,
@@ -260,8 +289,8 @@ export class TauriClient implements APIClient {
260289 return data as ActionResult ;
261290 }
262291
263- async stopSlot ( sessionName : string , projectPath ?: string ) : Promise < ActionResult > {
264- return this . runAction ( "stop" , sessionName , projectPath ) ;
292+ async stopSlot ( sessionName : string , scope ?: WorkspaceScope | string ) : Promise < ActionResult > {
293+ return this . runAction ( "stop" , sessionName , scope ) ;
265294 }
266295
267296 async getApiInfo ( ) : Promise < { port : number ; config_path : string ; state_path : string } > {
@@ -276,9 +305,9 @@ export class TauriClient implements APIClient {
276305 return data . profiles as Profile [ ] ;
277306 }
278307
279- async initWorkspace ( profile : string , bootstrapSessions : boolean , projectPath ?: string ) : Promise < InitResult > {
308+ async initWorkspace ( profile : string , bootstrapSessions : boolean , scope ?: WorkspaceScope | string ) : Promise < InitResult > {
280309 const baseUrl = await this . _baseUrl ( ) ;
281- const res = await fetch ( `${ baseUrl } /api/init${ qs ( projectPath ) } ` , {
310+ const res = await fetch ( `${ baseUrl } /api/init${ qs ( scope ) } ` , {
282311 method : "POST" ,
283312 headers : { "Content-Type" : "application/json" } ,
284313 body : JSON . stringify ( { profile, bootstrap_sessions : bootstrapSessions } ) ,
@@ -290,12 +319,12 @@ export class TauriClient implements APIClient {
290319
291320 async saveConfig (
292321 content : string ,
293- projectPath ?: string ,
322+ scope ?: WorkspaceScope | string ,
294323 baseMtime ?: number | null ,
295324 baseContentHash ?: string | null
296325 ) : Promise < ConfigSaveResult > {
297326 const baseUrl = await this . _baseUrl ( ) ;
298- const res = await fetch ( `${ baseUrl } /api/config${ qs ( projectPath ) } ` , {
327+ const res = await fetch ( `${ baseUrl } /api/config${ qs ( scope ) } ` , {
299328 method : "POST" ,
300329 headers : { "Content-Type" : "application/json" } ,
301330 body : JSON . stringify ( {
0 commit comments