Przeglądaj źródła

add project.name/icon

Dax Raad 2 miesięcy temu
rodzic
commit
fb1b6c5e6b

+ 2 - 1
packages/opencode/src/file/watcher.ts

@@ -8,6 +8,7 @@ import { Config } from "../config/config"
 import { createWrapper } from "@parcel/watcher/wrapper"
 import { lazy } from "@/util/lazy"
 import type ParcelWatcher from "@parcel/watcher"
+import { $ } from "bun"
 
 declare const OPENCODE_LIBC: string | undefined
 
@@ -65,7 +66,7 @@ export namespace FileWatcher {
         }),
       )
 
-      const vcsDir = Instance.project.vcsDir
+      const vcsDir = await $`git rev-parse --git-dir`.quiet().nothrow().cwd(Instance.worktree).text()
       if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
         subs.push(
           await watcher().subscribe(vcsDir, subscribe, {

+ 18 - 10
packages/opencode/src/project/project.ts

@@ -7,6 +7,7 @@ import { Log } from "../util/log"
 import { Flag } from "@/flag/flag"
 import { Session } from "../session"
 import { work } from "../util/queue"
+import { fn } from "@opencode-ai/util/fn"
 
 export namespace Project {
   const log = Log.create({ service: "project" })
@@ -14,8 +15,9 @@ export namespace Project {
     .object({
       id: z.string(),
       worktree: z.string(),
-      vcsDir: z.string().optional(),
       vcs: z.literal("git").optional(),
+      name: z.string().optional(),
+      icon: z.string().optional(),
       time: z.object({
         created: z.number(),
         updated: z.number().optional(),
@@ -46,7 +48,6 @@ export namespace Project {
       return project
     }
     let worktree = path.dirname(git)
-    const timer = log.time("git.rev-parse")
     let id = await Bun.file(path.join(git, "opencode"))
       .text()
       .then((x) => x.trim())
@@ -67,19 +68,12 @@ export namespace Project {
       id = roots[0]
       if (id) Bun.file(path.join(git, "opencode")).write(id)
     }
-    timer.stop()
     worktree = await $`git rev-parse --show-toplevel`
       .quiet()
       .nothrow()
       .cwd(worktree)
       .text()
       .then((x) => path.resolve(worktree, x.trim()))
-    const vcsDir = await $`git rev-parse --git-dir`
-      .quiet()
-      .nothrow()
-      .cwd(worktree)
-      .text()
-      .then((x) => path.resolve(worktree, x.trim()))
     const projectID = id || "global"
     const existing = id ? await Storage.read<Info>(["project", id]).catch(() => undefined) : undefined
     if (!existing && id) {
@@ -89,7 +83,6 @@ export namespace Project {
       ...existing,
       id: projectID,
       worktree,
-      vcsDir,
       vcs: "git",
       time: {
         created: Date.now(),
@@ -135,4 +128,19 @@ export namespace Project {
     const keys = await Storage.list(["project"])
     return await Promise.all(keys.map((x) => Storage.read<Info>(x)))
   }
+
+  export const update = fn(
+    z.object({
+      projectID: z.string(),
+      name: z.string().optional(),
+      icon: z.string().optional(),
+    }),
+    async (input) => {
+      return await Storage.update<Info>(["project", input.projectID], (draft) => {
+        if (input.name !== undefined) draft.name = input.name
+        if (input.icon !== undefined) draft.icon = input.icon
+        draft.time.updated = Date.now()
+      })
+    },
+  )
 }

+ 2 - 4
packages/opencode/src/project/vcs.ts

@@ -39,16 +39,14 @@ export namespace Vcs {
 
   const state = Instance.state(
     async () => {
-      const vcsDir = Instance.project.vcsDir
-      if (Instance.project.vcs !== "git" || !vcsDir) {
+      if (Instance.project.vcs !== "git") {
         return { branch: async () => undefined, unsubscribe: undefined }
       }
       let current = await currentBranch()
       log.info("initialized", { branch: current })
 
-      const head = path.join(vcsDir, "HEAD")
       const unsubscribe = Bus.subscribe(FileWatcher.Event.Updated, async (evt) => {
-        if (evt.properties.file !== head) return
+        if (evt.properties.file.endsWith("HEAD")) return
         const next = await currentBranch()
         if (next !== current) {
           log.info("branch changed", { from: current, to: next })

+ 30 - 1
packages/opencode/src/server/project.ts

@@ -1,8 +1,10 @@
 import { Hono } from "hono"
-import { describeRoute } from "hono-openapi"
+import { describeRoute, validator } from "hono-openapi"
 import { resolver } from "hono-openapi"
 import { Instance } from "../project/instance"
 import { Project } from "../project/project"
+import z from "zod"
+import { errors } from "./error"
 
 export const ProjectRoute = new Hono()
   .get(
@@ -48,3 +50,30 @@ export const ProjectRoute = new Hono()
       return c.json(Instance.project)
     },
   )
+  .patch(
+    "/:projectID",
+    describeRoute({
+      summary: "Update project",
+      description: "Update project properties such as name and icon.",
+      operationId: "project.update",
+      responses: {
+        200: {
+          description: "Updated project information",
+          content: {
+            "application/json": {
+              schema: resolver(Project.Info),
+            },
+          },
+        },
+        ...errors(400, 404),
+      },
+    }),
+    validator("param", z.object({ projectID: z.string() })),
+    validator("json", Project.update.schema.omit({ projectID: true })),
+    async (c) => {
+      const projectID = c.req.valid("param").projectID
+      const body = c.req.valid("json")
+      const project = await Project.update({ ...body, projectID })
+      return c.json(project)
+    },
+  )

+ 0 - 1
packages/opencode/test/project/project.test.ts

@@ -18,7 +18,6 @@ describe("Project.fromDirectory", () => {
     expect(project.id).toBe("global")
     expect(project.vcs).toBe("git")
     expect(project.worktree).toBe(tmp.path)
-    expect(project.vcsDir).toBe(path.join(tmp.path, ".git"))
 
     const opencodeFile = path.join(tmp.path, ".git", "opencode")
     const fileExists = await Bun.file(opencodeFile).exists()