mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-21 18:13:26 +00:00
db87b47754
* fix(cli): pin server URL in .pad.toml for remote workspaces (BUG-1535) Two fixes: 1. Replace stale api.getpad.dev references with app.getpad.dev in the --url flag help, NewClientFromURL doc, and Config.URL doc. Also fix internal/mcp/dispatch_http.go's comment to use the canonical mcp.getpad.dev/mcp URL. 2. Persist the server URL into .pad.toml when linking a directory to a non-local workspace. WriteWorkspaceLink now takes a serverURL arg; pad init / workspace link / workspace switch pass cfg.BaseURL() when Mode != local. getConfig() reads .pad.toml's URL as an override above ~/.pad/config.toml and below the --url flag, so commands like `pad collection list` from a remote-linked directory hit the right server without --url on every call. Passing --url explicitly also promotes local → remote so the directory pin is written even when the existing global config has mode=local. * fix(cli): scope .pad.toml URL override to client paths per Codex review (round 1) Round 1 review flagged that applying the .pad.toml URL override inside getConfig() contaminates server/admin commands: pad server start would advertise the wrong PublicLinkBaseURL, and pad auth setup would refuse to run locally because Mode flipped to remote. Extract the override into applyPadTomlOverride() and call it only from client-API entry points — getConfiguredConfig() and the pad init client phase. Server/admin commands (pad server start/stop, pad auth setup, pad auth configure) keep using raw getConfig() and are unaffected. Also skip the override when --url was explicitly passed (LoadedFromFlags), so the flag retains unambiguous priority. * fix(cli): preserve .pad.toml URL on workspace link/switch per Codex review (round 2) Round 2 review noted workspace link / workspace switch reached the server via getClient() (override applied) but then wrote the new .pad.toml URL using a raw getConfig() — which would drop or miswrite the url field when relinking inside a remote-pinned directory whose global config is local. Reuse the cfg returned by getClient() for padTomlURLFor so the write matches the API client.
103 lines
2.7 KiB
Go
103 lines
2.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
)
|
|
|
|
// PadToml represents the per-project workspace link file.
|
|
type PadToml struct {
|
|
Workspace string `toml:"workspace"`
|
|
// URL is the base URL of the Pad server hosting this workspace (e.g.
|
|
// "https://app.getpad.dev" or a self-hosted remote). When set, it
|
|
// overrides the user's global ~/.pad/config.toml URL so the directory
|
|
// targets the right server regardless of which workspace the user's
|
|
// default config points at. Empty for local-mode workspaces (the
|
|
// default loopback server is implied). See BUG-1535.
|
|
URL string `toml:"url,omitempty"`
|
|
AgentName string `toml:"agent_name,omitempty"` // optional: identifies which AI agent is acting
|
|
}
|
|
|
|
// DetectWorkspace walks up the directory tree from cwd looking for .pad.toml.
|
|
func DetectWorkspace(flagOverride string) (string, error) {
|
|
if flagOverride != "" {
|
|
return flagOverride, nil
|
|
}
|
|
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
dir := cwd
|
|
for {
|
|
configPath := filepath.Join(dir, ".pad.toml")
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
var cfg PadToml
|
|
if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
|
|
return "", fmt.Errorf("parse %s: %w", configPath, err)
|
|
}
|
|
if cfg.Workspace != "" {
|
|
return cfg.Workspace, nil
|
|
}
|
|
}
|
|
|
|
parent := filepath.Dir(dir)
|
|
if parent == dir {
|
|
break
|
|
}
|
|
dir = parent
|
|
}
|
|
|
|
return "", fmt.Errorf("no workspace linked. Run 'pad workspace init' to create one")
|
|
}
|
|
|
|
// LoadPadToml finds and reads the nearest .pad.toml by walking up from cwd.
|
|
// Returns nil if no .pad.toml is found (not an error).
|
|
func LoadPadToml() (*PadToml, error) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dir := cwd
|
|
for {
|
|
configPath := filepath.Join(dir, ".pad.toml")
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
var cfg PadToml
|
|
if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse %s: %w", configPath, err)
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
parent := filepath.Dir(dir)
|
|
if parent == dir {
|
|
break
|
|
}
|
|
dir = parent
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// WriteWorkspaceLink writes a .pad.toml in the given directory.
|
|
//
|
|
// serverURL is the base URL of the Pad server hosting this workspace. Pass
|
|
// the empty string for local-mode workspaces; pass cfg.BaseURL() (or
|
|
// equivalently cfg.URL) for any non-local mode (remote, cloud) so that the
|
|
// directory targets the right server even when the user's global config
|
|
// points at a different one. See BUG-1535.
|
|
func WriteWorkspaceLink(dir, slug, serverURL string) error {
|
|
path := filepath.Join(dir, ".pad.toml")
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return toml.NewEncoder(f).Encode(PadToml{Workspace: slug, URL: serverURL})
|
|
}
|