Skip to content

Commit 21dc12a

Browse files
committed
feat: client-generated UUIDs and streaming model metadata
- All API Create methods now accept client-provided UUIDs - Use uuid.UUID type for type safety - Add MessageStart/MessageStop block types for stream lifecycle - Extract model from message_start, stop_reason from message_stop - Fix PowerSync connection pooling with dedicated controller connection - Add UpdateMeta for setting model/stop_reason on assistant messages
1 parent 9775926 commit 21dc12a

34 files changed

Lines changed: 404 additions & 89 deletions

‎AGENTS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ TUI presentation? → internal/tui/
3333
```bash
3434
task do # Format, lint, test - run before commits
3535
task run # Fast iteration
36+
tail -f /tmp/tero.log # Watch logs
3637
```

‎internal/api/account_service.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/log"
78
"github.com/usetero/cli/pkg/client"
89
)
@@ -11,7 +12,7 @@ import (
1112
type Accounts interface {
1213
List(ctx context.Context, organizationID string) ([]Account, error)
1314
Get(ctx context.Context, accountID string) (*Account, error)
14-
Create(ctx context.Context, organizationID, name string) (*Account, error)
15+
Create(ctx context.Context, id uuid.UUID, organizationID, name string) (*Account, error)
1516
}
1617

1718
// AccountService handles account-related API operations.
@@ -81,10 +82,11 @@ func (s *AccountService) Get(ctx context.Context, accountID string) (*Account, e
8182
}, nil
8283
}
8384

84-
// Create creates a new account.
85-
func (s *AccountService) Create(ctx context.Context, organizationID, name string) (*Account, error) {
86-
s.logger.Debug("creating account via API", "organizationID", organizationID, "name", name)
85+
// Create creates a new account with the given client-provided ID.
86+
func (s *AccountService) Create(ctx context.Context, id uuid.UUID, organizationID, name string) (*Account, error) {
87+
s.logger.Debug("creating account via API", "id", id.String(), "organizationID", organizationID, "name", name)
8788
input := client.CreateAccountInput{
89+
Id: id.String(),
8890
OrganizationID: organizationID,
8991
Name: name,
9092
}

‎internal/api/account_service_test.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"testing"
77

8+
"github.com/google/uuid"
89
"github.com/usetero/cli/internal/api"
910
"github.com/usetero/cli/internal/api/apitest"
1011
"github.com/usetero/cli/internal/log/logtest"
@@ -106,7 +107,8 @@ func TestAccountService_Create(t *testing.T) {
106107
}
107108

108109
svc := api.NewAccountService(mockClient, logtest.New(t))
109-
account, err := svc.Create(context.Background(), "org-123", "New Account")
110+
testID := uuid.New()
111+
account, err := svc.Create(context.Background(), testID, "org-123", "New Account")
110112

111113
if err != nil {
112114
t.Fatalf("unexpected error: %v", err)
@@ -131,7 +133,7 @@ func TestAccountService_Create(t *testing.T) {
131133
}
132134

133135
svc := api.NewAccountService(mockClient, logtest.New(t))
134-
_, err := svc.Create(context.Background(), "org-123", "Test")
136+
_, err := svc.Create(context.Background(), uuid.New(), "org-123", "Test")
135137

136138
if err == nil {
137139
t.Fatal("expected error, got nil")

‎internal/api/apitest/factories.go‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package apitest
22

3-
import "github.com/usetero/cli/internal/api"
3+
import (
4+
"github.com/google/uuid"
5+
"github.com/usetero/cli/internal/api"
6+
)
47

58
// NewOrganization creates a test organization with sensible defaults.
69
// Use functional options to override specific fields.
710
func NewOrganization(opts ...func(*api.Organization)) api.Organization {
811
org := api.Organization{
9-
ID: "org-test",
12+
ID: uuid.New().String(),
1013
Name: "Test Organization",
1114
}
1215
for _, opt := range opts {
@@ -19,7 +22,7 @@ func NewOrganization(opts ...func(*api.Organization)) api.Organization {
1922
// Use functional options to override specific fields.
2023
func NewAccount(opts ...func(*api.Account)) api.Account {
2124
acc := api.Account{
22-
ID: "acc-test",
25+
ID: uuid.New().String(),
2326
Name: "Test Account",
2427
}
2528
for _, opt := range opts {
@@ -32,7 +35,7 @@ func NewAccount(opts ...func(*api.Account)) api.Account {
3235
// Use functional options to override specific fields.
3336
func NewWorkspace(opts ...func(*api.Workspace)) api.Workspace {
3437
ws := api.Workspace{
35-
ID: "ws-test",
38+
ID: uuid.New().String(),
3639
Name: "Test Workspace",
3740
}
3841
for _, opt := range opts {

‎internal/api/apitest/mock_accounts.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package apitest
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/api"
78
)
89

910
// MockAccounts implements api.Accounts for testing.
1011
type MockAccounts struct {
1112
ListFunc func(ctx context.Context, organizationID string) ([]api.Account, error)
1213
GetFunc func(ctx context.Context, accountID string) (*api.Account, error)
13-
CreateFunc func(ctx context.Context, organizationID, name string) (*api.Account, error)
14+
CreateFunc func(ctx context.Context, id uuid.UUID, organizationID, name string) (*api.Account, error)
1415
}
1516

1617
func (m *MockAccounts) List(ctx context.Context, organizationID string) ([]api.Account, error) {
@@ -27,9 +28,9 @@ func (m *MockAccounts) Get(ctx context.Context, accountID string) (*api.Account,
2728
return nil, nil
2829
}
2930

30-
func (m *MockAccounts) Create(ctx context.Context, organizationID, name string) (*api.Account, error) {
31+
func (m *MockAccounts) Create(ctx context.Context, id uuid.UUID, organizationID, name string) (*api.Account, error) {
3132
if m.CreateFunc != nil {
32-
return m.CreateFunc(ctx, organizationID, name)
33+
return m.CreateFunc(ctx, id, organizationID, name)
3334
}
3435
return nil, nil
3536
}

‎internal/api/apitest/mock_conversations.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@ package apitest
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/api"
78
)
89

910
// MockConversations implements api.Conversations for testing.
1011
type MockConversations struct {
11-
CreateFunc func(ctx context.Context, workspaceID, title string) (*api.Conversation, error)
12+
CreateFunc func(ctx context.Context, id uuid.UUID, workspaceID, title string) (*api.Conversation, error)
1213
UpdateFunc func(ctx context.Context, id, title string) (*api.Conversation, error)
1314
DeleteFunc func(ctx context.Context, id string) error
1415
}
1516

16-
func (m *MockConversations) Create(ctx context.Context, workspaceID, title string) (*api.Conversation, error) {
17+
func (m *MockConversations) Create(ctx context.Context, id uuid.UUID, workspaceID, title string) (*api.Conversation, error) {
1718
if m.CreateFunc != nil {
18-
return m.CreateFunc(ctx, workspaceID, title)
19+
return m.CreateFunc(ctx, id, workspaceID, title)
1920
}
2021
return nil, nil
2122
}

‎internal/api/apitest/mock_datadog_accounts.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package apitest
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/api"
78
)
89

@@ -11,7 +12,7 @@ type MockDatadogAccounts struct {
1112
HasAccountFunc func(ctx context.Context, accountID string) (bool, error)
1213
GetAccountFunc func(ctx context.Context, accountID string) (*api.DatadogAccount, error)
1314
ValidateAPIKeyFunc func(ctx context.Context, apiKey, site string) (bool, string, error)
14-
CreateAccountFunc func(ctx context.Context, accountID, name, site, apiKey, appKey string) (*api.DatadogAccount, error)
15+
CreateAccountFunc func(ctx context.Context, id uuid.UUID, accountID, name, site, apiKey, appKey string) (*api.DatadogAccount, error)
1516
GetStatusFunc func(ctx context.Context, datadogAccountID string) (*api.DatadogAccountStatus, error)
1617
}
1718

@@ -36,9 +37,9 @@ func (m *MockDatadogAccounts) ValidateAPIKey(ctx context.Context, apiKey, site s
3637
return false, "", nil
3738
}
3839

39-
func (m *MockDatadogAccounts) CreateAccount(ctx context.Context, accountID, name, site, apiKey, appKey string) (*api.DatadogAccount, error) {
40+
func (m *MockDatadogAccounts) CreateAccount(ctx context.Context, id uuid.UUID, accountID, name, site, apiKey, appKey string) (*api.DatadogAccount, error) {
4041
if m.CreateAccountFunc != nil {
41-
return m.CreateAccountFunc(ctx, accountID, name, site, apiKey, appKey)
42+
return m.CreateAccountFunc(ctx, id, accountID, name, site, apiKey, appKey)
4243
}
4344
return nil, nil
4445
}

‎internal/api/apitest/mock_organizations.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package apitest
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/api"
78
)
89

910
// MockOrganizations implements api.Organizations for testing.
1011
type MockOrganizations struct {
1112
ListFunc func(ctx context.Context) ([]api.Organization, error)
12-
CreateFunc func(ctx context.Context, name string) (*api.OrganizationBootstrapResult, error)
13+
CreateFunc func(ctx context.Context, id uuid.UUID, name string) (*api.OrganizationBootstrapResult, error)
1314
}
1415

1516
func (m *MockOrganizations) List(ctx context.Context) ([]api.Organization, error) {
@@ -19,9 +20,9 @@ func (m *MockOrganizations) List(ctx context.Context) ([]api.Organization, error
1920
return nil, nil
2021
}
2122

22-
func (m *MockOrganizations) Create(ctx context.Context, name string) (*api.OrganizationBootstrapResult, error) {
23+
func (m *MockOrganizations) Create(ctx context.Context, id uuid.UUID, name string) (*api.OrganizationBootstrapResult, error) {
2324
if m.CreateFunc != nil {
24-
return m.CreateFunc(ctx, name)
25+
return m.CreateFunc(ctx, id, name)
2526
}
2627
return nil, nil
2728
}

‎internal/api/conversation_service.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package api
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/log"
78
"github.com/usetero/cli/pkg/client"
89
)
910

1011
// Conversations provides access to conversations.
1112
type Conversations interface {
12-
Create(ctx context.Context, workspaceID, title string) (*Conversation, error)
13+
Create(ctx context.Context, id uuid.UUID, workspaceID, title string) (*Conversation, error)
1314
Update(ctx context.Context, id, title string) (*Conversation, error)
1415
Delete(ctx context.Context, id string) error
1516
}
@@ -38,11 +39,12 @@ type Conversation struct {
3839
Title string
3940
}
4041

41-
// Create creates a new conversation.
42-
func (s *ConversationService) Create(ctx context.Context, workspaceID, title string) (*Conversation, error) {
43-
s.logger.Debug("creating conversation via API", "workspaceID", workspaceID, "title", title)
42+
// Create creates a new conversation with the given client-provided ID.
43+
func (s *ConversationService) Create(ctx context.Context, id uuid.UUID, workspaceID, title string) (*Conversation, error) {
44+
s.logger.Debug("creating conversation via API", "id", id.String(), "workspaceID", workspaceID, "title", title)
4445

4546
input := client.CreateConversationInput{
47+
Id: id.String(),
4648
WorkspaceID: workspaceID,
4749
Title: title,
4850
}

‎internal/api/datadog_account_service.go‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"context"
55

6+
"github.com/google/uuid"
67
"github.com/usetero/cli/internal/log"
78
"github.com/usetero/cli/pkg/client"
89
)
@@ -12,7 +13,7 @@ type DatadogAccounts interface {
1213
HasAccount(ctx context.Context, accountID string) (bool, error)
1314
GetAccount(ctx context.Context, accountID string) (*DatadogAccount, error)
1415
ValidateAPIKey(ctx context.Context, apiKey, site string) (bool, string, error)
15-
CreateAccount(ctx context.Context, accountID, name, site, apiKey, appKey string) (*DatadogAccount, error)
16+
CreateAccount(ctx context.Context, id uuid.UUID, accountID, name, site, apiKey, appKey string) (*DatadogAccount, error)
1617
GetStatus(ctx context.Context, datadogAccountID string) (*DatadogAccountStatus, error)
1718
}
1819

@@ -155,10 +156,11 @@ func (s *DatadogAccountService) ValidateAPIKey(ctx context.Context, apiKey, site
155156
// Both API key and Application key must be provided.
156157
// Keys are sent to control plane and stored securely there - never stored locally.
157158
// The control plane validates the credentials before creating the account.
158-
func (s *DatadogAccountService) CreateAccount(ctx context.Context, accountID, name, site, apiKey, appKey string) (*DatadogAccount, error) {
159-
s.logger.Debug("creating datadog account with credentials via API", "accountID", accountID, "site", site)
159+
func (s *DatadogAccountService) CreateAccount(ctx context.Context, id uuid.UUID, accountID, name, site, apiKey, appKey string) (*DatadogAccount, error) {
160+
s.logger.Debug("creating datadog account with credentials via API", "id", id.String(), "accountID", accountID, "site", site)
160161
input := client.CreateDatadogAccountWithCredentialsInput{
161162
Attributes: client.CreateDatadogAccountInput{
163+
Id: id.String(),
162164
AccountID: accountID,
163165
Name: name,
164166
Site: client.DatadogAccountSite(site), // US1, US5, EU1, etc.

0 commit comments

Comments
 (0)