parent/child mode

This commit is contained in:
Egor Aristov 2025-10-23 17:58:09 +03:00
parent 332a9e5eab
commit ebb37ce5b8
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
3 changed files with 72 additions and 7 deletions

8
CLAUDE.md Normal file
View File

@ -0,0 +1,8 @@
For all prompts:
- if you need to understand what project does, feel free to walk over files and analyze them
Common recommendations:
- dont write unnesesary comments if code is obvious
Recommendations on golang code style:
- avoid iota

View File

@ -32,7 +32,7 @@ func main() {
cmdStr := fmt.Sprintf("node %s", path.Join(cwd, "..", "ts/index.js")) cmdStr := fmt.Sprintf("node %s", path.Join(cwd, "..", "ts/index.js"))
cmd := exec.Command(cmdStr) cmd := exec.Command(cmdStr)
kit, err := kittenipc.New(cmd, &api, kittenipc.Config{}) kit, err := kittenipc.NewParent(cmd, &api)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }

View File

@ -2,6 +2,7 @@ package kittenipc
import ( import (
"bufio" "bufio"
"flag"
"fmt" "fmt"
"net" "net"
"os" "os"
@ -16,14 +17,20 @@ import (
"github.com/samber/mo" "github.com/samber/mo"
) )
const ipcSocketArg = "--ipc-socket"
type StdioMode int type StdioMode int
type Config struct { type ipcMode int
}
const (
modeParent ipcMode = 1
modeChild ipcMode = 2
)
type KittenIPC struct { type KittenIPC struct {
mode ipcMode
cmd *exec.Cmd cmd *exec.Cmd
cfg Config
localApi any localApi any
socketPath string socketPath string
@ -36,12 +43,13 @@ type KittenIPC struct {
mu sync.Mutex mu sync.Mutex
} }
func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) { func NewParent(cmd *exec.Cmd, localApi any) (*KittenIPC, error) {
k := KittenIPC{ k := KittenIPC{
mode: modeParent,
cmd: cmd, cmd: cmd,
cfg: cfg,
localApi: localApi, localApi: localApi,
pendingCalls: make(map[int64]chan callResult), pendingCalls: make(map[int64]chan callResult),
errCh: make(chan error, 1),
} }
k.socketPath = filepath.Join(os.TempDir(), fmt.Sprintf("kitten-ipc-%d.sock", os.Getpid())) k.socketPath = filepath.Join(os.TempDir(), fmt.Sprintf("kitten-ipc-%d.sock", os.Getpid()))
@ -49,7 +57,6 @@ func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) {
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
const ipcSocketArg = "--ipc-socket"
if slices.Contains(cmd.Args, ipcSocketArg) { if slices.Contains(cmd.Args, ipcSocketArg) {
return nil, fmt.Errorf("you should not use `%s` argument in your command", ipcSocketArg) return nil, fmt.Errorf("you should not use `%s` argument in your command", ipcSocketArg)
} }
@ -60,7 +67,33 @@ func New(cmd *exec.Cmd, localApi any, cfg Config) (*KittenIPC, error) {
return &k, nil return &k, nil
} }
func NewChild(localApi any) (*KittenIPC, error) {
k := KittenIPC{
mode: modeChild,
localApi: localApi,
pendingCalls: make(map[int64]chan callResult),
errCh: make(chan error, 1),
}
socketPath := flag.String("ipc-socket", "", "Path to IPC socket")
flag.Parse()
if *socketPath == "" {
return nil, fmt.Errorf("ipc socket path is missing")
}
k.socketPath = *socketPath
return &k, nil
}
func (k *KittenIPC) Start() error { func (k *KittenIPC) Start() error {
if k.mode == modeParent {
return k.startParent()
}
return k.startChild()
}
func (k *KittenIPC) startParent() error {
_ = os.Remove(k.socketPath) _ = os.Remove(k.socketPath)
listener, err := net.Listen("unix", k.socketPath) listener, err := net.Listen("unix", k.socketPath)
if err != nil { if err != nil {
@ -81,6 +114,16 @@ func (k *KittenIPC) Start() error {
return nil return nil
} }
func (k *KittenIPC) startChild() error {
conn, err := net.Dial("unix", k.socketPath)
if err != nil {
return fmt.Errorf("connect to parent socket: %w", err)
}
k.conn = conn
k.startRcvData()
return nil
}
func (k *KittenIPC) acceptConn() error { func (k *KittenIPC) acceptConn() error {
const acceptTimeout = time.Second * 10 const acceptTimeout = time.Second * 10
@ -289,7 +332,13 @@ func (k *KittenIPC) closeSock() error {
} }
func (k *KittenIPC) Wait() error { func (k *KittenIPC) Wait() error {
if k.mode == modeParent {
return k.waitParent()
}
return k.waitChild()
}
func (k *KittenIPC) waitParent() error {
waitErrCh := make(chan error, 1) waitErrCh := make(chan error, 1)
go func() { go func() {
@ -310,6 +359,14 @@ func (k *KittenIPC) Wait() error {
return nil return nil
} }
func (k *KittenIPC) waitChild() error {
err := <-k.errCh
if err != nil {
return fmt.Errorf("ipc error: %w", err)
}
return nil
}
func mergeErr(errs ...error) (ret error) { func mergeErr(errs ...error) (ret error) {
for _, err := range errs { for _, err := range errs {
if err != nil { if err != nil {