Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/store/builtin_tool_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ type BuiltinToolStore interface {
Get(ctx context.Context, name string) (*BuiltinToolDef, error)
Update(ctx context.Context, name string, updates map[string]any) error
Seed(ctx context.Context, tools []BuiltinToolDef) error
// Upsert inserts/updates tools without reconcile — safe for additive fork-specific tools.
// Does NOT delete rows not in the provided list.
Upsert(ctx context.Context, tools []BuiltinToolDef) error
ListEnabled(ctx context.Context) ([]BuiltinToolDef, error)
GetSettings(ctx context.Context, name string) (json.RawMessage, error)
}
51 changes: 51 additions & 0 deletions internal/store/pg/builtin_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,57 @@ func (s *PGBuiltinToolStore) Seed(ctx context.Context, tools []store.BuiltinTool
return tx.Commit()
}

// Upsert inserts or updates builtin tool definitions without reconcile DELETE.
// Safe for additive fork-specific tools -- does NOT delete rows not in the list.
func (s *PGBuiltinToolStore) Upsert(ctx context.Context, tools []store.BuiltinToolDef) error {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()

stmt, err := tx.PrepareContext(ctx,
`INSERT INTO builtin_tools (name, display_name, description, category, enabled, settings, requires, metadata, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $9)
ON CONFLICT (name) DO UPDATE SET
display_name = EXCLUDED.display_name,
description = EXCLUDED.description,
category = EXCLUDED.category,
requires = EXCLUDED.requires,
metadata = EXCLUDED.metadata,
settings = CASE
WHEN builtin_tools.settings IS NULL OR builtin_tools.settings::text IN ('{}', 'null')
THEN EXCLUDED.settings
ELSE builtin_tools.settings
END,
updated_at = EXCLUDED.updated_at`)
if err != nil {
return fmt.Errorf("prepare upsert stmt: %w", err)
}
defer stmt.Close()

now := time.Now()
for _, t := range tools {
settings := t.Settings
if settings == nil {
settings = json.RawMessage("{}")
}
metadata := t.Metadata
if metadata == nil {
metadata = json.RawMessage("{}")
}
_, err := stmt.ExecContext(ctx,
t.Name, t.DisplayName, t.Description, t.Category,
t.Enabled, []byte(settings), pqStringArray(t.Requires), []byte(metadata), now,
)
if err != nil {
return fmt.Errorf("upsert tool %s: %w", t.Name, err)
}
}

return tx.Commit()
}

func (s *PGBuiltinToolStore) scanTool(row *sql.Row) (*store.BuiltinToolDef, error) {
var def store.BuiltinToolDef
var settings []byte
Expand Down
51 changes: 51 additions & 0 deletions internal/store/sqlitestore/builtin-tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,57 @@ func (s *SQLiteBuiltinToolStore) Seed(ctx context.Context, tools []store.Builtin
return tx.Commit()
}

// Upsert inserts or updates builtin tool definitions without reconcile DELETE.
// Safe for additive fork-specific tools -- does NOT delete rows not in the list.
func (s *SQLiteBuiltinToolStore) Upsert(ctx context.Context, tools []store.BuiltinToolDef) error {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()

stmt, err := tx.PrepareContext(ctx,
`INSERT INTO builtin_tools (name, display_name, description, category, enabled, settings, requires, metadata, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (name) DO UPDATE SET
display_name = excluded.display_name,
description = excluded.description,
category = excluded.category,
requires = excluded.requires,
metadata = excluded.metadata,
settings = CASE
WHEN builtin_tools.settings IS NULL OR builtin_tools.settings IN ('{}', 'null')
THEN excluded.settings
ELSE builtin_tools.settings
END,
updated_at = excluded.updated_at`)
if err != nil {
return fmt.Errorf("prepare upsert stmt: %w", err)
}
defer stmt.Close()

now := time.Now()
for _, t := range tools {
settings := t.Settings
if settings == nil {
settings = json.RawMessage("{}")
}
metadata := t.Metadata
if metadata == nil {
metadata = json.RawMessage("{}")
}
_, err := stmt.ExecContext(ctx,
t.Name, t.DisplayName, t.Description, t.Category,
t.Enabled, []byte(settings), jsonStringArray(t.Requires), []byte(metadata), now, now,
)
if err != nil {
return fmt.Errorf("upsert tool %s: %w", t.Name, err)
}
}

return tx.Commit()
}

func (s *SQLiteBuiltinToolStore) scanTool(row *sql.Row) (*store.BuiltinToolDef, error) {
var def store.BuiltinToolDef
var settings []byte
Expand Down