Dax Raad 9 months ago
parent
commit
42c1cd6a85

+ 0 - 11
js/src/bus/index.ts

@@ -30,17 +30,6 @@ export namespace Bus {
     return result;
   }
 
-  export function payloads() {
-    return registry
-      .entries()
-      .map(([type, def]) =>
-        z.object({
-          type: z.string("hey"),
-        }),
-      )
-      .toArray();
-  }
-
   export function specs() {
     const children = {} as any;
     for (const [type, def] of registry.entries()) {

+ 1 - 7
js/src/index.ts

@@ -1,3 +1,4 @@
+import "zod-openapi/extend";
 import { App } from "./app";
 import { Server } from "./server/server";
 import fs from "fs/promises";
@@ -15,13 +16,6 @@ cli.command("", "Start the opencode in interactive mode").action(async () => {
   await App.provide({ directory: process.cwd() }, async () => {
     await Share.init();
     Server.listen();
-
-    Bun.spawnSync({
-      stderr: "inherit",
-      stdout: "inherit",
-      stdin: "inherit",
-      cmd: ["go", "run", "cmd/main.go"],
-    });
   });
 });
 

+ 0 - 1
js/src/server/server.ts

@@ -6,7 +6,6 @@ import { streamSSE } from "hono/streaming";
 import { Session } from "../session/session";
 import { resolver, validator as zValidator } from "hono-openapi/zod";
 import { z } from "zod";
-import "zod-openapi/extend";
 import { Config } from "../app/config";
 import { LLM } from "../llm/llm";
 import { Message } from "../session/message";

+ 11 - 0
js/src/session/message.ts

@@ -1,6 +1,17 @@
 import z from "zod";
+import { z as zv4 } from "zod/v4";
+import { Bus } from "../bus";
 
 export namespace Message {
+  export const Event = {
+    Updated: Bus.event(
+      "message.updated",
+      zv4.object({
+        sessionID: zv4.string(),
+        messageID: zv4.string(),
+      }),
+    ),
+  };
   export const ToolCall = z
     .object({
       state: z.literal("call"),

+ 23 - 2
js/src/session/session.ts

@@ -11,6 +11,7 @@ import {
   streamText,
 } from "ai";
 import { z } from "zod";
+import { z as zv4 } from "zod/v4";
 import * as tools from "../tool";
 import { Decimal } from "decimal.js";
 
@@ -18,7 +19,8 @@ import PROMPT_ANTHROPIC from "./prompt/anthropic.txt";
 import PROMPT_TITLE from "./prompt/title.txt";
 
 import { Share } from "../share/share";
-import type { Message } from "./message";
+import { Message } from "./message";
+import { Bus } from "../bus";
 
 export namespace Session {
   const log = Log.create({ service: "session" });
@@ -30,6 +32,15 @@ export namespace Session {
   });
   export type Info = z.output<typeof Info>;
 
+  export const Event = {
+    Updated: Bus.event(
+      "session.updated",
+      zv4.object({
+        sessionID: zv4.string(),
+      }),
+    ),
+  };
+
   const state = App.state("session", () => {
     const sessions = new Map<string, Info>();
     const messages = new Map<string, Message.Info[]>();
@@ -49,6 +60,9 @@ export namespace Session {
     state().sessions.set(result.id, result);
     await Storage.writeJSON("session/info/" + result.id, result);
     await share(result.id);
+    Bus.publish(Event.Updated, {
+      sessionID: result.id,
+    });
     return result;
   }
 
@@ -80,6 +94,9 @@ export namespace Session {
     editor(session);
     sessions.set(id, session);
     await Storage.writeJSON("session/info/" + id, session);
+    Bus.publish(Event.Updated, {
+      sessionID: id,
+    });
     return session;
   }
 
@@ -126,10 +143,14 @@ export namespace Session {
     const model = await LLM.findModel(input.providerID, input.modelID);
     const msgs = await messages(input.sessionID);
     async function write(msg: Message.Info) {
-      return Storage.writeJSON(
+      await Storage.writeJSON(
         "session/message/" + input.sessionID + "/" + msg.id,
         msg,
       );
+      Bus.publish(Message.Event.Updated, {
+        sessionID: input.sessionID,
+        messageID: msg.id,
+      });
     }
     const app = await App.use();
     if (msgs.length === 0) {

+ 3 - 1
pkg/client/event.go

@@ -10,7 +10,9 @@ import (
 )
 
 var EventMap = map[string]any{
-	"storage.write": EventStorageWrite{},
+	"storage.write":   EventStorageWrite{},
+	"session.updated": EventSessionUpdated{},
+	"message.updated": EventMessageUpdated{},
 }
 
 type EventMessage struct {

+ 26 - 0
pkg/client/gen/event.json

@@ -29,6 +29,32 @@
         "serverID",
         "path"
       ]
+    },
+    "event.message.updated": {
+      "type": "object",
+      "properties": {
+        "sessionID": {
+          "type": "string"
+        },
+        "messageID": {
+          "type": "string"
+        }
+      },
+      "required": [
+        "sessionID",
+        "messageID"
+      ]
+    },
+    "event.session.updated": {
+      "type": "object",
+      "properties": {
+        "sessionID": {
+          "type": "string"
+        }
+      },
+      "required": [
+        "sessionID"
+      ]
     }
   }
 }

+ 52 - 0
pkg/client/generated-event.go

@@ -34,6 +34,58 @@ func (j *EventLspClientDiagnostics) UnmarshalJSON(value []byte) error {
 	return nil
 }
 
+type EventMessageUpdated struct {
+	// MessageID corresponds to the JSON schema field "messageID".
+	MessageID string `json:"messageID" yaml:"messageID" mapstructure:"messageID"`
+
+	// SessionID corresponds to the JSON schema field "sessionID".
+	SessionID string `json:"sessionID" yaml:"sessionID" mapstructure:"sessionID"`
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (j *EventMessageUpdated) UnmarshalJSON(value []byte) error {
+	var raw map[string]interface{}
+	if err := json.Unmarshal(value, &raw); err != nil {
+		return err
+	}
+	if _, ok := raw["messageID"]; raw != nil && !ok {
+		return fmt.Errorf("field messageID in EventMessageUpdated: required")
+	}
+	if _, ok := raw["sessionID"]; raw != nil && !ok {
+		return fmt.Errorf("field sessionID in EventMessageUpdated: required")
+	}
+	type Plain EventMessageUpdated
+	var plain Plain
+	if err := json.Unmarshal(value, &plain); err != nil {
+		return err
+	}
+	*j = EventMessageUpdated(plain)
+	return nil
+}
+
+type EventSessionUpdated struct {
+	// SessionID corresponds to the JSON schema field "sessionID".
+	SessionID string `json:"sessionID" yaml:"sessionID" mapstructure:"sessionID"`
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (j *EventSessionUpdated) UnmarshalJSON(value []byte) error {
+	var raw map[string]interface{}
+	if err := json.Unmarshal(value, &raw); err != nil {
+		return err
+	}
+	if _, ok := raw["sessionID"]; raw != nil && !ok {
+		return fmt.Errorf("field sessionID in EventSessionUpdated: required")
+	}
+	type Plain EventSessionUpdated
+	var plain Plain
+	if err := json.Unmarshal(value, &plain); err != nil {
+		return err
+	}
+	*j = EventSessionUpdated(plain)
+	return nil
+}
+
 type EventStorageWrite struct {
 	// Content corresponds to the JSON schema field "content".
 	Content interface{} `json:"content" yaml:"content" mapstructure:"content"`