Просмотр исходного кода

chore(app): global config changes

adamelmore 3 недель назад
Родитель
Сommit
2f35c40bb5

+ 84 - 24
packages/app/src/context/global-sync.tsx

@@ -188,7 +188,74 @@ function createGlobalSync() {
     config: {},
     reload: undefined,
   })
-  let bootstrapQueue: string[] = []
+
+  const queued = new Set<string>()
+  let root = false
+  let running = false
+  let timer: ReturnType<typeof setTimeout> | undefined
+
+  const paused = () => untrack(() => globalStore.reload) !== undefined
+
+  const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
+
+  const take = (count: number) => {
+    if (queued.size === 0) return [] as string[]
+    const items: string[] = []
+    for (const item of queued) {
+      queued.delete(item)
+      items.push(item)
+      if (items.length >= count) break
+    }
+    return items
+  }
+
+  const schedule = () => {
+    if (timer) return
+    timer = setTimeout(() => {
+      timer = undefined
+      void drain()
+    }, 0)
+  }
+
+  const push = (directory: string) => {
+    if (!directory) return
+    queued.add(directory)
+    if (paused()) return
+    schedule()
+  }
+
+  const refresh = () => {
+    root = true
+    if (paused()) return
+    schedule()
+  }
+
+  async function drain() {
+    if (running) return
+    running = true
+    try {
+      while (true) {
+        if (paused()) return
+
+        if (root) {
+          root = false
+          await bootstrap()
+          await tick()
+          continue
+        }
+
+        const dirs = take(2)
+        if (dirs.length === 0) return
+
+        await Promise.all(dirs.map((dir) => bootstrapInstance(dir)))
+        await tick()
+      }
+    } finally {
+      running = false
+      if (paused()) return
+      if (root || queued.size) schedule()
+    }
+  }
 
   createEffect(() => {
     if (!projectCacheReady()) return
@@ -210,14 +277,8 @@ function createGlobalSync() {
 
   createEffect(() => {
     if (globalStore.reload !== "complete") return
-    if (bootstrapQueue.length) {
-      for (const directory of bootstrapQueue) {
-        bootstrapInstance(directory)
-      }
-      bootstrap()
-    }
-    bootstrapQueue = []
     setGlobalStore("reload", undefined)
+    refresh()
   })
 
   const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
@@ -584,9 +645,8 @@ function createGlobalSync() {
     if (directory === "global") {
       switch (event?.type) {
         case "global.disposed": {
-          if (globalStore.reload) return
-          bootstrap()
-          break
+          refresh()
+          return
         }
         case "project.updated": {
           const result = Binary.search(globalStore.project, event.properties.id, (s) => s.id)
@@ -647,12 +707,8 @@ function createGlobalSync() {
 
     switch (event.type) {
       case "server.instance.disposed": {
-        if (globalStore.reload) {
-          bootstrapQueue.push(directory)
-          return
-        }
-        bootstrapInstance(directory)
-        break
+        push(directory)
+        return
       }
       case "session.created": {
         const info = event.properties.info
@@ -893,6 +949,10 @@ function createGlobalSync() {
     }
   })
   onCleanup(unsub)
+  onCleanup(() => {
+    if (!timer) return
+    clearTimeout(timer)
+  })
 
   async function bootstrap() {
     const health = await globalSDK.client.global
@@ -916,7 +976,7 @@ function createGlobalSync() {
         }),
       ),
       retry(() =>
-        globalSDK.client.config.get().then((x) => {
+        globalSDK.client.global.config.get().then((x) => {
           setGlobalStore("config", x.data!)
         }),
       ),
@@ -999,13 +1059,13 @@ function createGlobalSync() {
     },
     child,
     bootstrap,
-    updateConfig: async (config: Config) => {
+    updateConfig: (config: Config) => {
       setGlobalStore("reload", "pending")
-      const response = await globalSDK.client.config.update({ config })
-      setTimeout(() => {
-        setGlobalStore("reload", "complete")
-      }, 1000)
-      return response
+      return globalSDK.client.global.config.update({ config }).finally(() => {
+        setTimeout(() => {
+          setGlobalStore("reload", "complete")
+        }, 1000)
+      })
     },
     project: {
       loadSessions,

+ 49 - 1
packages/opencode/src/server/routes/global.ts

@@ -1,5 +1,5 @@
 import { Hono } from "hono"
-import { describeRoute, resolver } from "hono-openapi"
+import { describeRoute, resolver, validator } from "hono-openapi"
 import { streamSSE } from "hono/streaming"
 import z from "zod"
 import { BusEvent } from "@/bus/bus-event"
@@ -8,6 +8,8 @@ import { Instance } from "../../project/instance"
 import { Installation } from "@/installation"
 import { Log } from "../../util/log"
 import { lazy } from "../../util/lazy"
+import { Config } from "../../config/config"
+import { errors } from "../error"
 
 const log = Log.create({ service: "server" })
 
@@ -103,6 +105,52 @@ export const GlobalRoutes = lazy(() =>
         })
       },
     )
+    .get(
+      "/config",
+      describeRoute({
+        summary: "Get global configuration",
+        description: "Retrieve the current global OpenCode configuration settings and preferences.",
+        operationId: "global.config.get",
+        responses: {
+          200: {
+            description: "Get global config info",
+            content: {
+              "application/json": {
+                schema: resolver(Config.Info),
+              },
+            },
+          },
+        },
+      }),
+      async (c) => {
+        return c.json(await Config.getGlobal())
+      },
+    )
+    .patch(
+      "/config",
+      describeRoute({
+        summary: "Update global configuration",
+        description: "Update global OpenCode configuration settings and preferences.",
+        operationId: "global.config.update",
+        responses: {
+          200: {
+            description: "Successfully updated global config",
+            content: {
+              "application/json": {
+                schema: resolver(Config.Info),
+              },
+            },
+          },
+          ...errors(400),
+        },
+      }),
+      validator("json", Config.Info),
+      async (c) => {
+        const config = c.req.valid("json")
+        await Config.updateGlobal(config)
+        return c.json(await Config.getGlobal())
+      },
+    )
     .post(
       "/dispose",
       describeRoute({

+ 52 - 6
packages/sdk/js/src/v2/gen/sdk.gen.ts

@@ -14,7 +14,7 @@ import type {
   AuthSetErrors,
   AuthSetResponses,
   CommandListResponses,
-  Config as Config2,
+  Config as Config3,
   ConfigGetResponses,
   ConfigProvidersResponses,
   ConfigUpdateErrors,
@@ -34,6 +34,9 @@ import type {
   FindSymbolsResponses,
   FindTextResponses,
   FormatterStatusResponses,
+  GlobalConfigGetResponses,
+  GlobalConfigUpdateErrors,
+  GlobalConfigUpdateResponses,
   GlobalDisposeResponses,
   GlobalEventResponses,
   GlobalHealthResponses,
@@ -215,6 +218,44 @@ class HeyApiRegistry<T> {
   }
 }
 
+export class Config extends HeyApiClient {
+  /**
+   * Get global configuration
+   *
+   * Retrieve the current global OpenCode configuration settings and preferences.
+   */
+  public get<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
+    return (options?.client ?? this.client).get<GlobalConfigGetResponses, unknown, ThrowOnError>({
+      url: "/global/config",
+      ...options,
+    })
+  }
+
+  /**
+   * Update global configuration
+   *
+   * Update global OpenCode configuration settings and preferences.
+   */
+  public update<ThrowOnError extends boolean = false>(
+    parameters?: {
+      config?: Config3
+    },
+    options?: Options<never, ThrowOnError>,
+  ) {
+    const params = buildClientParams([parameters], [{ args: [{ key: "config", map: "body" }] }])
+    return (options?.client ?? this.client).patch<GlobalConfigUpdateResponses, GlobalConfigUpdateErrors, ThrowOnError>({
+      url: "/global/config",
+      ...options,
+      ...params,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+        ...params.headers,
+      },
+    })
+  }
+}
+
 export class Global extends HeyApiClient {
   /**
    * Get health
@@ -251,6 +292,11 @@ export class Global extends HeyApiClient {
       ...options,
     })
   }
+
+  private _config?: Config
+  get config(): Config {
+    return (this._config ??= new Config({ client: this.client }))
+  }
 }
 
 export class Project extends HeyApiClient {
@@ -541,7 +587,7 @@ export class Pty extends HeyApiClient {
   }
 }
 
-export class Config extends HeyApiClient {
+export class Config2 extends HeyApiClient {
   /**
    * Get configuration
    *
@@ -569,7 +615,7 @@ export class Config extends HeyApiClient {
   public update<ThrowOnError extends boolean = false>(
     parameters?: {
       directory?: string
-      config?: Config2
+      config?: Config3
     },
     options?: Options<never, ThrowOnError>,
   ) {
@@ -3168,9 +3214,9 @@ export class OpencodeClient extends HeyApiClient {
     return (this._pty ??= new Pty({ client: this.client }))
   }
 
-  private _config?: Config
-  get config(): Config {
-    return (this._config ??= new Config({ client: this.client }))
+  private _config?: Config2
+  get config(): Config2 {
+    return (this._config ??= new Config2({ client: this.client }))
   }
 
   private _tool?: Tool

+ 56 - 15
packages/sdk/js/src/v2/gen/types.gen.ts

@@ -930,21 +930,6 @@ export type GlobalEvent = {
   payload: Event
 }
 
-export type BadRequestError = {
-  data: unknown
-  errors: Array<{
-    [key: string]: unknown
-  }>
-  success: false
-}
-
-export type NotFoundError = {
-  name: "NotFoundError"
-  data: {
-    message: string
-  }
-}
-
 /**
  * Custom keybind configurations
  */
@@ -1826,6 +1811,21 @@ export type Config = {
   }
 }
 
+export type BadRequestError = {
+  data: unknown
+  errors: Array<{
+    [key: string]: unknown
+  }>
+  success: false
+}
+
+export type NotFoundError = {
+  name: "NotFoundError"
+  data: {
+    message: string
+  }
+}
+
 export type Model = {
   id: string
   providerID: string
@@ -2199,6 +2199,47 @@ export type GlobalEventResponses = {
 
 export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
 
+export type GlobalConfigGetData = {
+  body?: never
+  path?: never
+  query?: never
+  url: "/global/config"
+}
+
+export type GlobalConfigGetResponses = {
+  /**
+   * Get global config info
+   */
+  200: Config
+}
+
+export type GlobalConfigGetResponse = GlobalConfigGetResponses[keyof GlobalConfigGetResponses]
+
+export type GlobalConfigUpdateData = {
+  body?: Config
+  path?: never
+  query?: never
+  url: "/global/config"
+}
+
+export type GlobalConfigUpdateErrors = {
+  /**
+   * Bad request
+   */
+  400: BadRequestError
+}
+
+export type GlobalConfigUpdateError = GlobalConfigUpdateErrors[keyof GlobalConfigUpdateErrors]
+
+export type GlobalConfigUpdateResponses = {
+  /**
+   * Successfully updated global config
+   */
+  200: Config
+}
+
+export type GlobalConfigUpdateResponse = GlobalConfigUpdateResponses[keyof GlobalConfigUpdateResponses]
+
 export type GlobalDisposeData = {
   body?: never
   path?: never

Разница между файлами не показана из-за своего большого размера
+ 401 - 82
packages/sdk/openapi.json


Некоторые файлы не были показаны из-за большого количества измененных файлов