Преглед изворни кода

Merge branch 'dev' into kit/dev-memory-observe

Kit Langton пре 2 недеља
родитељ
комит
6e68a927f2

+ 3 - 1
.github/workflows/test.yml

@@ -105,15 +105,17 @@ jobs:
         run: bun --cwd packages/app test:e2e:local
         env:
           CI: true
+          PLAYWRIGHT_JUNIT_OUTPUT: e2e/junit-${{ matrix.settings.name }}.xml
         timeout-minutes: 30
 
       - name: Upload Playwright artifacts
-        if: failure()
+        if: always()
         uses: actions/upload-artifact@v4
         with:
           name: playwright-${{ matrix.settings.name }}-${{ github.run_attempt }}
           if-no-files-found: ignore
           retention-days: 7
           path: |
+            packages/app/e2e/junit-*.xml
             packages/app/e2e/test-results
             packages/app/e2e/playwright-report

+ 6 - 1
packages/app/playwright.config.ts

@@ -7,6 +7,11 @@ const serverPort = process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"
 const command = `bun run dev -- --host 0.0.0.0 --port ${port}`
 const reuse = !process.env.CI
 const workers = Number(process.env.PLAYWRIGHT_WORKERS ?? (process.env.CI ? 5 : 0)) || undefined
+const reporter = [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]] as const
+
+if (process.env.PLAYWRIGHT_JUNIT_OUTPUT) {
+  reporter.push(["junit", { outputFile: process.env.PLAYWRIGHT_JUNIT_OUTPUT }])
+}
 
 export default defineConfig({
   testDir: "./e2e",
@@ -19,7 +24,7 @@ export default defineConfig({
   forbidOnly: !!process.env.CI,
   retries: process.env.CI ? 2 : 0,
   workers,
-  reporter: [["html", { outputFolder: "e2e/playwright-report", open: "never" }], ["line"]],
+  reporter,
   webServer: {
     command,
     url: baseURL,

+ 63 - 25
packages/opencode/src/session/todo.ts

@@ -1,6 +1,8 @@
 import { BusEvent } from "@/bus/bus-event"
 import { Bus } from "@/bus"
+import { makeRuntime } from "@/effect/run-service"
 import { SessionID } from "./schema"
+import { Effect, Layer, ServiceMap } from "effect"
 import z from "zod"
 import { Database, eq, asc } from "../storage/db"
 import { TodoTable } from "./session.sql"
@@ -25,33 +27,69 @@ export namespace Todo {
     ),
   }
 
-  export function update(input: { sessionID: SessionID; todos: Info[] }) {
-    Database.transaction((db) => {
-      db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
-      if (input.todos.length === 0) return
-      db.insert(TodoTable)
-        .values(
-          input.todos.map((todo, position) => ({
-            session_id: input.sessionID,
-            content: todo.content,
-            status: todo.status,
-            priority: todo.priority,
-            position,
-          })),
+  export interface Interface {
+    readonly update: (input: { sessionID: SessionID; todos: Info[] }) => Effect.Effect<void>
+    readonly get: (sessionID: SessionID) => Effect.Effect<Info[]>
+  }
+
+  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionTodo") {}
+
+  export const layer = Layer.effect(
+    Service,
+    Effect.gen(function* () {
+      const bus = yield* Bus.Service
+
+      const update = Effect.fn("Todo.update")(function* (input: { sessionID: SessionID; todos: Info[] }) {
+        yield* Effect.sync(() =>
+          Database.transaction((db) => {
+            db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
+            if (input.todos.length === 0) return
+            db.insert(TodoTable)
+              .values(
+                input.todos.map((todo, position) => ({
+                  session_id: input.sessionID,
+                  content: todo.content,
+                  status: todo.status,
+                  priority: todo.priority,
+                  position,
+                })),
+              )
+              .run()
+          }),
         )
-        .run()
-    })
-    Bus.publish(Event.Updated, input)
+        yield* bus.publish(Event.Updated, input)
+      })
+
+      const get = Effect.fn("Todo.get")(function* (sessionID: SessionID) {
+        const rows = yield* Effect.sync(() =>
+          Database.use((db) =>
+            db
+              .select()
+              .from(TodoTable)
+              .where(eq(TodoTable.session_id, sessionID))
+              .orderBy(asc(TodoTable.position))
+              .all(),
+          ),
+        )
+        return rows.map((row) => ({
+          content: row.content,
+          status: row.status,
+          priority: row.priority,
+        }))
+      })
+
+      return Service.of({ update, get })
+    }),
+  )
+
+  const defaultLayer = layer.pipe(Layer.provide(Bus.layer))
+  const { runPromise } = makeRuntime(Service, defaultLayer)
+
+  export async function update(input: { sessionID: SessionID; todos: Info[] }) {
+    return runPromise((svc) => svc.update(input))
   }
 
-  export function get(sessionID: SessionID) {
-    const rows = Database.use((db) =>
-      db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(),
-    )
-    return rows.map((row) => ({
-      content: row.content,
-      status: row.status,
-      priority: row.priority,
-    }))
+  export async function get(sessionID: SessionID) {
+    return runPromise((svc) => svc.get(sessionID))
   }
 }

+ 1 - 1
packages/opencode/src/tool/todo.ts

@@ -16,7 +16,7 @@ export const TodoWriteTool = Tool.define("todowrite", {
       metadata: {},
     })
 
-    Todo.update({
+    await Todo.update({
       sessionID: ctx.sessionID,
       todos: params.todos,
     })