package main import ( "context" "encoding/json" "errors" "fmt" "io" "os/exec" "syscall" ) type object = map[string]any type rpcError struct { Code int `json:"code"` Message string `json:"message"` } type event struct { ID json.RawMessage `json:"id"` Method string `json:"method"` Params json.RawMessage `json:"params"` Result json.RawMessage `json:"result"` Error *rpcError `json:"error"` } type client struct { cmd *exec.Cmd input *json.Encoder output *json.Decoder id int queued []event } func (b *bridge) start(ctx context.Context) (*client, error) { args := []string{"app-server"} if b.mcpURL != "" { url, err := json.Marshal(b.mcpURL) if err != nil { return nil, err } args = append(args, "-c", "mcp_servers.gateway.url="+string(url), "-c", "mcp_servers.gateway.required=true") } cmd := exec.CommandContext(ctx, b.binary, args...) cmd.Dir = b.dir cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} cmd.Cancel = func() error { return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) } in, err := cmd.StdinPipe() if err != nil { return nil, err } out, err := cmd.StdoutPipe() if err != nil { _ = in.Close() return nil, err } if err := cmd.Start(); err != nil { _ = in.Close() _ = out.Close() return nil, err } c := &client{cmd: cmd, input: json.NewEncoder(in), output: json.NewDecoder(out)} if _, err := c.call("initialize", object{"clientInfo": object{"name": "codex_bridge", "version": "0.1.0"}, "capabilities": object{"experimentalApi": true}}); err != nil { c.close() return nil, err } if err := c.input.Encode(object{"method": "initialized", "params": object{}}); err != nil { c.close() return nil, err } return c, nil } func (c *client) close() { _ = syscall.Kill(-c.cmd.Process.Pid, syscall.SIGKILL) _ = c.cmd.Wait() } func (c *client) read() (event, error) { var e event if err := c.output.Decode(&e); err != nil { if errors.Is(err, io.EOF) { return e, errors.New("codex app-server closed its output") } return e, err } if len(e.ID) > 0 && e.Method != "" { return e, fmt.Errorf("unsupported Codex client request: %s", e.Method) } return e, nil } func (c *client) call(method string, params object) (json.RawMessage, error) { c.id++ id := fmt.Sprint(c.id) if err := c.input.Encode(object{"id": c.id, "method": method, "params": params}); err != nil { return nil, err } for { e, err := c.read() if err != nil { return nil, err } if string(e.ID) != id { c.queued = append(c.queued, e) continue } if e.Error != nil { return nil, fmt.Errorf("%s: %s", method, e.Error.Message) } return e.Result, nil } } func (c *client) next() (event, error) { if len(c.queued) == 0 { return c.read() } e := c.queued[0] c.queued = c.queued[1:] return e, nil }