Skip to content
Merged
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
20 changes: 18 additions & 2 deletions cmd/pnf/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,16 @@ Commands:

Examples:
pnf tui --logdir ./logs
pnf tui --url https://api.example.com --api-key p95_xxx --logdir ./logs
pnf ls --logdir ./logs
pnf ls --logdir ./logs --project demo-project
pnf show <run-id> --logdir ./logs
pnf serve --logdir ./logs

Options:
--logdir Directory containing logs (default: ~/.p95/logs)
--url Remote API base URL (tui command; fallback: P95_URL)
--api-key Remote API key (tui command; fallback: P95_API_KEY)
--help Show this help message`)
}

Expand Down Expand Up @@ -336,12 +339,20 @@ func showCmd(args []string) {
func tuiCmd(args []string) {
fs := flag.NewFlagSet("tui", flag.ExitOnError)
logdir := fs.String("logdir", "", "Directory containing logs")
remoteURL := fs.String("url", "", "Remote API base URL")
apiKey := fs.String("api-key", "", "Remote API key")
fs.Parse(args)

if *logdir == "" {
*logdir = defaultLogDir()
}
*logdir = expandPath(*logdir)
if *remoteURL == "" {
*remoteURL = os.Getenv("P95_URL")
}
if *apiKey == "" {
*apiKey = os.Getenv("P95_API_KEY")
}

// Disable all logging - it breaks the TUI
log.SetOutput(io.Discard)
Expand Down Expand Up @@ -370,8 +381,13 @@ func tuiCmd(args []string) {
// Wait for server to be ready
time.Sleep(100 * time.Millisecond)

// Create API client pointing at local server
apiClient := client.New(fmt.Sprintf("http://%s", addr))
// Create API clients
localClient := client.New(fmt.Sprintf("http://%s", addr))
var apiClient client.API = localClient
if *remoteURL != "" {
remoteClient := client.NewWithAPIKey(*remoteURL, *apiKey)
apiClient = client.NewComposite(localClient, remoteClient)
}

// Create and run TUI
app := tui.New(apiClient)
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// App is the main TUI application model
type App struct {
client *client.Client
client client.API
width int
height int

Expand All @@ -30,7 +30,7 @@ type App struct {
}

// New creates a new TUI application
func New(apiClient *client.Client) App {
func New(apiClient client.API) App {
zone.NewGlobal()
return App{
client: apiClient,
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/views/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (

// DashboardModel is the dashboard view model
type DashboardModel struct {
client *client.Client
client client.API
width int
height int

Expand All @@ -39,7 +39,7 @@ type DashboardModel struct {
}

// NewDashboard creates a new dashboard model
func NewDashboard(c *client.Client) DashboardModel {
func NewDashboard(c client.API) DashboardModel {
return DashboardModel{
client: c,
focus: FocusTeams,
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/views/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const (

// MainModel is the unified main view model with lazygit-style layout
type MainModel struct {
client *client.Client
client client.API
width int
height int

Expand Down Expand Up @@ -102,7 +102,7 @@ type MainModel struct {
}

// NewMain creates a new main model
func NewMain(c *client.Client) MainModel {
func NewMain(c client.API) MainModel {
return MainModel{
client: c,
zoneID: zone.NewPrefix(),
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/views/run_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type MetricInfo struct {

// RunDetailModel is the run detail view model
type RunDetailModel struct {
client *client.Client
client client.API
width int
height int

Expand Down Expand Up @@ -68,7 +68,7 @@ type RunDetailModel struct {
}

// NewRunDetail creates a new run detail model
func NewRunDetail(c *client.Client) RunDetailModel {
func NewRunDetail(c client.API) RunDetailModel {
return RunDetailModel{
client: c,
charts: make(map[string]*components.Chart),
Expand Down
4 changes: 2 additions & 2 deletions internal/tui/views/runs_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

// RunsListModel is the runs list view model
type RunsListModel struct {
client *client.Client
client client.API
width int
height int

Expand All @@ -28,7 +28,7 @@ type RunsListModel struct {
}

// NewRunsList creates a new runs list model
func NewRunsList(c *client.Client) RunsListModel {
func NewRunsList(c client.API) RunsListModel {
return RunsListModel{
client: c,
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/url"
"strings"
"time"

"github.com/google/uuid"
Expand All @@ -15,13 +16,21 @@ import (
// Client is the API client for the TUI
type Client struct {
baseURL string
apiKey string
httpClient *http.Client
}

// New creates a new API client
func New(baseURL string) *Client {
return NewWithAPIKey(baseURL, "")
}

// NewWithAPIKey creates a new API client with optional bearer auth.
func NewWithAPIKey(baseURL, apiKey string) *Client {
baseURL = strings.TrimRight(baseURL, "/")
return &Client{
baseURL: baseURL,
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
Expand All @@ -45,6 +54,9 @@ func (c *Client) request(method, path string, body any) ([]byte, error) {
}

req.Header.Set("Content-Type", "application/json")
if c.apiKey != "" {
req.Header.Set("Authorization", "Bearer "+c.apiKey)
}

resp, err := c.httpClient.Do(req)
if err != nil {
Expand Down
Loading
Loading