feat(store): add Upsert() to BuiltinToolStore for additive tool seeding (#554)

Approved by github-maintain automation. Clean implementation of additive Upsert() for fork-specific tool seeding. Solves #336.
This commit is contained in:
Du Hoang Dinh authored and GitHub committed 2026-07-09 22:54:37 +07:00
1 parent dc4cfddd31
commit 519df3dd17
3 files changed
+105

No files matched your search

+3
View File
@@ -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
View File
@@ -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
@@ -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