Files
tutti/services/tuttid/data/workspace/sqlite_agent_quick_prompts.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

270 lines
9.9 KiB
Go

package workspace
import (
"context"
"database/sql"
"errors"
"fmt"
agentquickpromptbiz "github.com/tutti-os/tutti/services/tuttid/biz/agentquickprompt"
)
func (s *SQLiteStore) ListAgentQuickPrompts(ctx context.Context) ([]agentquickpromptbiz.Prompt, error) {
if s == nil || s.readDB == nil {
return nil, errors.New("workspace database is not initialized")
}
rows, err := s.readDB.QueryContext(ctx, `
SELECT id, title, content, version, created_at_unix_ms, updated_at_unix_ms
FROM agent_quick_prompts
ORDER BY sort_order ASC, id ASC
`)
if err != nil {
return nil, fmt.Errorf("list agent quick prompts: %w", err)
}
defer rows.Close()
prompts := make([]agentquickpromptbiz.Prompt, 0)
for rows.Next() {
var prompt agentquickpromptbiz.Prompt
if err := rows.Scan(&prompt.ID, &prompt.Title, &prompt.Content, &prompt.Version, &prompt.CreatedAtUnixMS, &prompt.UpdatedAtUnixMS); err != nil {
return nil, fmt.Errorf("scan agent quick prompt: %w", err)
}
prompts = append(prompts, prompt)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate agent quick prompts: %w", err)
}
return prompts, nil
}
func (s *SQLiteStore) CountAgentQuickPrompts(ctx context.Context) (int, error) {
if s == nil || s.readDB == nil {
return 0, errors.New("workspace database is not initialized")
}
var count int
if err := s.readDB.QueryRowContext(ctx, `SELECT COUNT(*) FROM agent_quick_prompts`).Scan(&count); err != nil {
return 0, fmt.Errorf("count agent quick prompts: %w", err)
}
return count, nil
}
func (s *SQLiteStore) CreateAgentQuickPrompt(ctx context.Context, prompt agentquickpromptbiz.Prompt) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin create agent quick prompt: %w", err)
}
defer func() { _ = tx.Rollback() }()
var count int
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM agent_quick_prompts`).Scan(&count); err != nil {
return fmt.Errorf("count agent quick prompts before create: %w", err)
}
if count >= agentquickpromptbiz.MaxPrompts {
return agentquickpromptbiz.ErrLimitExceeded
}
if _, err := tx.ExecContext(ctx, `UPDATE agent_quick_prompts SET sort_order = sort_order + 1`); err != nil {
return fmt.Errorf("shift agent quick prompt order before create: %w", err)
}
_, err = tx.ExecContext(ctx, `
INSERT INTO agent_quick_prompts (
id, title, content, version, created_at_unix_ms, updated_at_unix_ms, sort_order
) VALUES (?, ?, ?, ?, ?, ?, 0)
`, prompt.ID, prompt.Title, prompt.Content, prompt.Version, prompt.CreatedAtUnixMS, prompt.UpdatedAtUnixMS)
if err != nil {
return fmt.Errorf("create agent quick prompt: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit create agent quick prompt: %w", err)
}
return nil
}
func (s *SQLiteStore) UpdateAgentQuickPrompt(ctx context.Context, prompt agentquickpromptbiz.Prompt, expectedVersion int64) (agentquickpromptbiz.Prompt, error) {
if s == nil || s.writeDB == nil {
return agentquickpromptbiz.Prompt{}, errors.New("workspace database is not initialized")
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return agentquickpromptbiz.Prompt{}, fmt.Errorf("begin update agent quick prompt: %w", err)
}
defer func() { _ = tx.Rollback() }()
result, err := tx.ExecContext(ctx, `
UPDATE agent_quick_prompts
SET title = ?, content = ?, version = ?, updated_at_unix_ms = ?
WHERE id = ? AND version = ?
`, prompt.Title, prompt.Content, prompt.Version, prompt.UpdatedAtUnixMS, prompt.ID, expectedVersion)
if err != nil {
return agentquickpromptbiz.Prompt{}, fmt.Errorf("update agent quick prompt: %w", err)
}
if err := classifyAgentQuickPromptFence(ctx, tx, prompt.ID, result); err != nil {
return agentquickpromptbiz.Prompt{}, err
}
var stored agentquickpromptbiz.Prompt
if err := tx.QueryRowContext(ctx, `
SELECT id, title, content, version, created_at_unix_ms, updated_at_unix_ms
FROM agent_quick_prompts WHERE id = ?
`, prompt.ID).Scan(&stored.ID, &stored.Title, &stored.Content, &stored.Version, &stored.CreatedAtUnixMS, &stored.UpdatedAtUnixMS); err != nil {
return agentquickpromptbiz.Prompt{}, fmt.Errorf("read updated agent quick prompt: %w", err)
}
if err := tx.Commit(); err != nil {
return agentquickpromptbiz.Prompt{}, fmt.Errorf("commit update agent quick prompt: %w", err)
}
return stored, nil
}
func (s *SQLiteStore) DeleteAgentQuickPrompt(ctx context.Context, id string, expectedVersion int64) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin delete agent quick prompt: %w", err)
}
defer func() { _ = tx.Rollback() }()
var deletedSortOrder int
if err := tx.QueryRowContext(ctx, `SELECT sort_order FROM agent_quick_prompts WHERE id = ?`, id).Scan(&deletedSortOrder); errors.Is(err, sql.ErrNoRows) {
return agentquickpromptbiz.ErrNotFound
} else if err != nil {
return fmt.Errorf("read deleted agent quick prompt order: %w", err)
}
result, err := tx.ExecContext(ctx, `DELETE FROM agent_quick_prompts WHERE id = ? AND version = ?`, id, expectedVersion)
if err != nil {
return fmt.Errorf("delete agent quick prompt: %w", err)
}
if err := classifyAgentQuickPromptFence(ctx, tx, id, result); err != nil {
return err
}
if _, err := tx.ExecContext(ctx, `UPDATE agent_quick_prompts SET sort_order = sort_order - 1 WHERE sort_order > ?`, deletedSortOrder); err != nil {
return fmt.Errorf("compact agent quick prompt order after delete: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit delete agent quick prompt: %w", err)
}
return nil
}
func (s *SQLiteStore) MoveAgentQuickPrompt(ctx context.Context, promptID string, beforePromptID *string, expectedVersion int64, updatedAtUnixMS int64) ([]agentquickpromptbiz.Prompt, bool, error) {
if s == nil || s.writeDB == nil {
return nil, false, errors.New("workspace database is not initialized")
}
tx, err := s.writeDB.BeginTx(ctx, nil)
if err != nil {
return nil, false, fmt.Errorf("begin move agent quick prompt: %w", err)
}
defer func() { _ = tx.Rollback() }()
prompts, err := listAgentQuickPromptsTx(ctx, tx)
if err != nil {
return nil, false, err
}
fromIndex := -1
for index := range prompts {
if prompts[index].ID == promptID {
fromIndex = index
break
}
}
if fromIndex < 0 {
return nil, false, agentquickpromptbiz.ErrNotFound
}
if prompts[fromIndex].Version != expectedVersion {
return nil, false, agentquickpromptbiz.ErrVersionConflict
}
if beforePromptID != nil && *beforePromptID == promptID {
if err := tx.Commit(); err != nil {
return nil, false, fmt.Errorf("commit idempotent agent quick prompt move: %w", err)
}
return prompts, false, nil
}
moving := prompts[fromIndex]
prompts = append(prompts[:fromIndex], prompts[fromIndex+1:]...)
insertIndex := len(prompts)
if beforePromptID != nil {
insertIndex = -1
for index := range prompts {
if prompts[index].ID == *beforePromptID {
insertIndex = index
break
}
}
if insertIndex < 0 {
return nil, false, agentquickpromptbiz.ErrOrderConflict
}
}
prompts = append(prompts, agentquickpromptbiz.Prompt{})
copy(prompts[insertIndex+1:], prompts[insertIndex:])
prompts[insertIndex] = moving
if insertIndex == fromIndex {
if err := tx.Commit(); err != nil {
return nil, false, fmt.Errorf("commit no-op agent quick prompt move: %w", err)
}
return prompts, false, nil
}
prompts[insertIndex].Version++
prompts[insertIndex].UpdatedAtUnixMS = updatedAtUnixMS
for index := range prompts {
if prompts[index].ID == promptID {
result, err := tx.ExecContext(ctx, `UPDATE agent_quick_prompts SET sort_order = ?, version = ?, updated_at_unix_ms = ? WHERE id = ? AND version = ?`, index, prompts[index].Version, updatedAtUnixMS, prompts[index].ID, expectedVersion)
if err != nil {
return nil, false, fmt.Errorf("rewrite moved agent quick prompt: %w", err)
}
if err := classifyAgentQuickPromptFence(ctx, tx, promptID, result); err != nil {
return nil, false, err
}
continue
}
if _, err := tx.ExecContext(ctx, `UPDATE agent_quick_prompts SET sort_order = ? WHERE id = ?`, index, prompts[index].ID); err != nil {
return nil, false, fmt.Errorf("rewrite agent quick prompt order: %w", err)
}
}
if err := tx.Commit(); err != nil {
return nil, false, fmt.Errorf("commit move agent quick prompt: %w", err)
}
return prompts, true, nil
}
func listAgentQuickPromptsTx(ctx context.Context, tx *sql.Tx) ([]agentquickpromptbiz.Prompt, error) {
rows, err := tx.QueryContext(ctx, `
SELECT id, title, content, version, created_at_unix_ms, updated_at_unix_ms
FROM agent_quick_prompts
ORDER BY sort_order ASC, id ASC
`)
if err != nil {
return nil, fmt.Errorf("list agent quick prompts in transaction: %w", err)
}
defer rows.Close()
prompts := make([]agentquickpromptbiz.Prompt, 0)
for rows.Next() {
var prompt agentquickpromptbiz.Prompt
if err := rows.Scan(&prompt.ID, &prompt.Title, &prompt.Content, &prompt.Version, &prompt.CreatedAtUnixMS, &prompt.UpdatedAtUnixMS); err != nil {
return nil, fmt.Errorf("scan agent quick prompt in transaction: %w", err)
}
prompts = append(prompts, prompt)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate agent quick prompts in transaction: %w", err)
}
return prompts, nil
}
func classifyAgentQuickPromptFence(ctx context.Context, tx *sql.Tx, id string, result sql.Result) error {
affected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("read agent quick prompt mutation result: %w", err)
}
if affected > 0 {
return nil
}
var version int64
err = tx.QueryRowContext(ctx, `SELECT version FROM agent_quick_prompts WHERE id = ?`, id).Scan(&version)
if errors.Is(err, sql.ErrNoRows) {
return agentquickpromptbiz.ErrNotFound
}
if err != nil {
return fmt.Errorf("check agent quick prompt mutation fence: %w", err)
}
return agentquickpromptbiz.ErrVersionConflict
}