11import * as vscode from "vscode"
2+ import * as fs from "fs/promises"
3+ import * as path from "path"
24
35import { logger } from "../utils/logging"
46import {
@@ -11,8 +13,22 @@ import {
1113 isSecretKey ,
1214 isGlobalStateKey ,
1315 isPassThroughStateKey ,
16+ globalStateSchema ,
1417} from "../shared/globalState"
15- import { API_CONFIG_KEYS , ApiConfiguration } from "../shared/api"
18+ import { API_CONFIG_KEYS , ApiConfiguration , apiHandlerOptionsSchema , ApiHandlerOptionsKey } from "../shared/api"
19+
20+ const NON_EXPORTABLE_GLOBAL_CONFIGURATION : GlobalStateKey [ ] = [
21+ "taskHistory" ,
22+ "listApiConfigMeta" ,
23+ "currentApiConfigName" ,
24+ ]
25+
26+ const NON_EXPORTABLE_API_CONFIGURATION : ApiHandlerOptionsKey [ ] = [
27+ "glamaModelInfo" ,
28+ "openRouterModelInfo" ,
29+ "unboundModelInfo" ,
30+ "requestyModelInfo" ,
31+ ]
1632
1733export class ContextProxy {
1834 private readonly originalContext : vscode . ExtensionContext
@@ -155,14 +171,125 @@ export class ContextProxy {
155171 // that the setting's value should be `undefined` and therefore we
156172 // need to remove it from the state cache if it exists.
157173 await this . setValues ( {
158- ...API_CONFIG_KEYS . filter ( ( key ) => ! ! this . stateCache . get ( key ) ) . reduce (
159- ( acc , key ) => ( { ...acc , [ key ] : undefined } ) ,
160- { } as Partial < ConfigurationValues > ,
161- ) ,
174+ ...API_CONFIG_KEYS . filter ( ( key ) => isGlobalStateKey ( key ) )
175+ . filter ( ( key ) => ! ! this . stateCache . get ( key ) )
176+ . reduce ( ( acc , key ) => ( { ...acc , [ key ] : undefined } ) , { } as Partial < ConfigurationValues > ) ,
162177 ...apiConfiguration ,
163178 } )
164179 }
165180
181+ private getAllGlobalStateValues ( ) {
182+ const values : Partial < Record < GlobalStateKey , any > > = { }
183+
184+ for ( const key of GLOBAL_STATE_KEYS ) {
185+ const value = this . getGlobalState ( key )
186+
187+ if ( value !== undefined ) {
188+ values [ key ] = value
189+ }
190+ }
191+
192+ return values
193+ }
194+
195+ private getAllSecretValues ( ) {
196+ const values : Partial < Record < SecretKey , string > > = { }
197+
198+ for ( const key of SECRET_KEYS ) {
199+ const value = this . getSecret ( key )
200+
201+ if ( value !== undefined ) {
202+ values [ key ] = value
203+ }
204+ }
205+
206+ return values
207+ }
208+
209+ async exportGlobalConfiguration ( filePath : string ) {
210+ try {
211+ const values = this . getAllGlobalStateValues ( )
212+ const configuration = globalStateSchema . parse ( values )
213+ const omit = new Set < string > ( [ ...API_CONFIG_KEYS , ...NON_EXPORTABLE_GLOBAL_CONFIGURATION ] )
214+ const entries = Object . entries ( configuration ) . filter ( ( [ key ] ) => ! omit . has ( key ) )
215+
216+ if ( entries . length === 0 ) {
217+ throw new Error ( "No configuration values to export." )
218+ }
219+
220+ const globalConfiguration = Object . fromEntries ( entries )
221+
222+ const dirname = path . dirname ( filePath )
223+ await fs . mkdir ( dirname , { recursive : true } )
224+ await fs . writeFile ( filePath , JSON . stringify ( globalConfiguration , null , 2 ) , "utf-8" )
225+ return globalConfiguration
226+ } catch ( error ) {
227+ console . log ( error . message )
228+ logger . error (
229+ `Error exporting global configuration to ${ filePath } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
230+ )
231+ return undefined
232+ }
233+ }
234+
235+ async importGlobalConfiguration ( filePath : string ) {
236+ try {
237+ const configuration = globalStateSchema . parse ( JSON . parse ( await fs . readFile ( filePath , "utf-8" ) ) )
238+ const omit = new Set < string > ( [ ...API_CONFIG_KEYS , ...NON_EXPORTABLE_GLOBAL_CONFIGURATION ] )
239+ const entries = Object . entries ( configuration ) . filter ( ( [ key ] ) => ! omit . has ( key ) )
240+
241+ if ( entries . length === 0 ) {
242+ throw new Error ( "No configuration values to import." )
243+ }
244+
245+ const globalConfiguration = Object . fromEntries ( entries )
246+ await this . setValues ( globalConfiguration )
247+ return globalConfiguration
248+ } catch ( error ) {
249+ logger . error (
250+ `Error importing global configuration from ${ filePath } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
251+ )
252+ return undefined
253+ }
254+ }
255+
256+ async exportApiConfiguration ( filePath : string ) {
257+ try {
258+ const apiConfiguration = apiHandlerOptionsSchema
259+ . omit ( NON_EXPORTABLE_API_CONFIGURATION . reduce ( ( acc , key ) => ( { ...acc , [ key ] : true } ) , { } ) )
260+ . parse ( {
261+ ...this . getAllGlobalStateValues ( ) ,
262+ ...this . getAllSecretValues ( ) ,
263+ } )
264+
265+ const dirname = path . dirname ( filePath )
266+ await fs . mkdir ( dirname , { recursive : true } )
267+ await fs . writeFile ( filePath , JSON . stringify ( apiConfiguration , null , 2 ) , "utf-8" )
268+ return apiConfiguration
269+ } catch ( error ) {
270+ logger . error (
271+ `Error exporting API configuration to ${ filePath } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
272+ )
273+ return undefined
274+ }
275+ }
276+
277+ async importApiConfiguration ( filePath : string ) {
278+ try {
279+ const apiConfiguration = apiHandlerOptionsSchema
280+ . omit ( NON_EXPORTABLE_API_CONFIGURATION . reduce ( ( acc , key ) => ( { ...acc , [ key ] : true } ) , { } ) )
281+ . parse ( JSON . parse ( await fs . readFile ( filePath , "utf-8" ) ) )
282+
283+ await this . setApiConfiguration ( apiConfiguration )
284+ return apiConfiguration
285+ } catch ( error ) {
286+ logger . error (
287+ `Error importing API configuration from ${ filePath } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
288+ )
289+ return undefined
290+ }
291+ }
292+
166293 /**
167294 * Resets all global state, secrets, and in-memory caches.
168295 * This clears all data from both the in-memory caches and the VSCode storage.
@@ -184,6 +311,6 @@ export class ContextProxy {
184311 // Wait for all reset operations to complete.
185312 await Promise . all ( [ ...stateResetPromises , ...secretResetPromises ] )
186313
187- this . initialize ( )
314+ await this . initialize ( )
188315 }
189316}
0 commit comments