refactor(runtime): add jane-ai compatibility paths\n\n- Centralize home and config path resolution with jane-ai-first fallbacks in pkg/runtimepaths\n- Update auth, skills, sessions, launcher config, and default workspace paths to honor JANE_AI_* and legacy PICOCLAW_* env vars\n- Add frontend compatibility for jane-ai session storage and OAuth postMessage identifiers while preserving legacy picoclaw keys\n- Add runtime path tests and update path-based assertions for the new ~/.jane-ai default\n- Tests passed for pkg/runtimepaths and pkg/auth targeted cases; broader go test is blocked by existing missing go.sum entries
This commit is contained in:
parent
badcbaeb97
commit
dd69cbeb16
16 changed files with 149 additions and 86 deletions
|
|
@ -2,14 +2,13 @@ package configstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
picoclawconfig "jane/pkg/config"
|
picoclawconfig "jane/pkg/config"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
configDirName = ".picoclaw"
|
|
||||||
configFileName = "config.json"
|
configFileName = "config.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -22,11 +21,7 @@ func ConfigPath() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigDir() (string, error) {
|
func ConfigDir() (string, error) {
|
||||||
home, err := os.UserHomeDir()
|
return runtimepaths.HomeDir(), nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.Join(home, configDirName), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Load() (*picoclawconfig.Config, error) {
|
func Load() (*picoclawconfig.Config, error) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"jane/cmd/picoclaw/internal"
|
"jane/cmd/picoclaw/internal"
|
||||||
"jane/pkg/config"
|
"jane/pkg/config"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCreateCommand() *cobra.Command {
|
func NewCreateCommand() *cobra.Command {
|
||||||
|
|
@ -65,7 +66,7 @@ func createAgentCmd(name, workspace, sysPrompt, model string, interactive bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
if workspace == "" {
|
if workspace == "" {
|
||||||
fmt.Printf("Workspace path (default: ~/.picoclaw/workspace/%s): ", strings.ToLower(strings.ReplaceAll(name, " ", "_")))
|
fmt.Printf("Workspace path (default: %s/workspace/%s): ", runtimepaths.HomeDir(), strings.ToLower(strings.ReplaceAll(name, " ", "_")))
|
||||||
workspaceInput, _ := reader.ReadString('\n')
|
workspaceInput, _ := reader.ReadString('\n')
|
||||||
workspaceInput = strings.TrimSpace(workspaceInput)
|
workspaceInput = strings.TrimSpace(workspaceInput)
|
||||||
if workspaceInput != "" {
|
if workspaceInput != "" {
|
||||||
|
|
@ -95,14 +96,7 @@ func createAgentCmd(name, workspace, sysPrompt, model string, interactive bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
if workspace == "" {
|
if workspace == "" {
|
||||||
var homePath string
|
workspace = filepath.Join(runtimepaths.HomeDir(), "workspace", strings.ToLower(strings.ReplaceAll(name, " ", "_")))
|
||||||
if picoclawHome := os.Getenv("PICOCLAW_HOME"); picoclawHome != "" {
|
|
||||||
homePath = picoclawHome
|
|
||||||
} else {
|
|
||||||
userHome, _ := os.UserHomeDir()
|
|
||||||
homePath = filepath.Join(userHome, ".picoclaw")
|
|
||||||
}
|
|
||||||
workspace = filepath.Join(homePath, "workspace", strings.ToLower(strings.ReplaceAll(name, " ", "_")))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelCfg *config.AgentModelConfig
|
var modelCfg *config.AgentModelConfig
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"jane/pkg/config"
|
"jane/pkg/config"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Logo = "🦞"
|
const Logo = "🦞"
|
||||||
|
|
@ -12,18 +12,11 @@ const Logo = "🦞"
|
||||||
// GetPicoclawHome returns the picoclaw home directory.
|
// GetPicoclawHome returns the picoclaw home directory.
|
||||||
// Priority: $PICOCLAW_HOME > ~/.picoclaw
|
// Priority: $PICOCLAW_HOME > ~/.picoclaw
|
||||||
func GetPicoclawHome() string {
|
func GetPicoclawHome() string {
|
||||||
if home := os.Getenv("PICOCLAW_HOME"); home != "" {
|
return runtimepaths.HomeDir()
|
||||||
return home
|
|
||||||
}
|
|
||||||
home, _ := os.UserHomeDir()
|
|
||||||
return filepath.Join(home, ".picoclaw")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigPath() string {
|
func GetConfigPath() string {
|
||||||
if configPath := os.Getenv("PICOCLAW_CONFIG"); configPath != "" {
|
return runtimepaths.ConfigPath()
|
||||||
return configPath
|
|
||||||
}
|
|
||||||
return filepath.Join(GetPicoclawHome(), "config.json")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (*config.Config, error) {
|
func LoadConfig() (*config.Config, error) {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func TestGetConfigPath(t *testing.T) {
|
||||||
t.Setenv("HOME", "/tmp/home")
|
t.Setenv("HOME", "/tmp/home")
|
||||||
|
|
||||||
got := GetConfigPath()
|
got := GetConfigPath()
|
||||||
want := filepath.Join("/tmp/home", ".picoclaw", "config.json")
|
want := filepath.Join("/tmp/home", ".jane-ai", "config.json")
|
||||||
|
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ func TestGetConfigPath_Windows(t *testing.T) {
|
||||||
t.Setenv("USERPROFILE", testUserProfilePath)
|
t.Setenv("USERPROFILE", testUserProfilePath)
|
||||||
|
|
||||||
got := GetConfigPath()
|
got := GetConfigPath()
|
||||||
want := filepath.Join(testUserProfilePath, ".picoclaw", "config.json")
|
want := filepath.Join(testUserProfilePath, ".jane-ai", "config.json")
|
||||||
|
|
||||||
require.True(t, strings.EqualFold(got, want), "GetConfigPath() = %q, want %q", got, want)
|
require.True(t, strings.EqualFold(got, want), "GetConfigPath() = %q, want %q", got, want)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"jane/pkg/config"
|
"jane/pkg/config"
|
||||||
"jane/pkg/logger"
|
"jane/pkg/logger"
|
||||||
"jane/pkg/providers"
|
"jane/pkg/providers"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
"jane/pkg/skills"
|
"jane/pkg/skills"
|
||||||
"jane/pkg/utils"
|
"jane/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
@ -52,20 +53,13 @@ func (cb *ContextBuilder) WithToolDiscovery(useBM25, useRegex bool) *ContextBuil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGlobalConfigDir() string {
|
func getGlobalConfigDir() string {
|
||||||
if home := os.Getenv("PICOCLAW_HOME"); home != "" {
|
return runtimepaths.HomeDir()
|
||||||
return home
|
|
||||||
}
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return filepath.Join(home, ".picoclaw")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextBuilder(workspace string) *ContextBuilder {
|
func NewContextBuilder(workspace string) *ContextBuilder {
|
||||||
// builtin skills: skills directory in current project
|
// builtin skills: skills directory in current project
|
||||||
// Use the skills/ directory under the current working directory
|
// Use the skills/ directory under the current working directory
|
||||||
builtinSkillsDir := strings.TrimSpace(os.Getenv("PICOCLAW_BUILTIN_SKILLS"))
|
builtinSkillsDir := strings.TrimSpace(runtimepaths.BuiltinSkillsOverride())
|
||||||
if builtinSkillsDir == "" {
|
if builtinSkillsDir == "" {
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
builtinSkillsDir = filepath.Join(wd, "skills")
|
builtinSkillsDir = filepath.Join(wd, "skills")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"jane/pkg/fileutil"
|
"jane/pkg/fileutil"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthCredential struct {
|
type AuthCredential struct {
|
||||||
|
|
@ -39,11 +40,7 @@ func (c *AuthCredential) NeedsRefresh() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func authFilePath() string {
|
func authFilePath() string {
|
||||||
if home := os.Getenv("PICOCLAW_HOME"); home != "" {
|
return filepath.Join(runtimepaths.HomeDir(), "auth.json")
|
||||||
return filepath.Join(home, "auth.json")
|
|
||||||
}
|
|
||||||
home, _ := os.UserHomeDir()
|
|
||||||
return filepath.Join(home, ".picoclaw", "auth.json")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadStore() (*AuthStore, error) {
|
func LoadStore() (*AuthStore, error) {
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func TestStoreFilePermissions(t *testing.T) {
|
||||||
t.Fatalf("SetCredential() error: %v", err)
|
t.Fatalf("SetCredential() error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(tmpDir, ".picoclaw", "auth.json")
|
path := filepath.Join(tmpDir, ".jane-ai", "auth.json")
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Stat() error: %v", err)
|
t.Fatalf("Stat() error: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/mdp/qrterminal/v3"
|
"github.com/mdp/qrterminal/v3"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"jane/pkg/logger"
|
"jane/pkg/logger"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
|
|
||||||
"go.mau.fi/mautrix-gmessages/pkg/libgm"
|
"go.mau.fi/mautrix-gmessages/pkg/libgm"
|
||||||
"go.mau.fi/mautrix-gmessages/pkg/libgm/events"
|
"go.mau.fi/mautrix-gmessages/pkg/libgm/events"
|
||||||
|
|
@ -30,11 +31,7 @@ type GMClient struct {
|
||||||
func (c *GMessagesChannel) initClient(ctx context.Context) error {
|
func (c *GMessagesChannel) initClient(ctx context.Context) error {
|
||||||
dataDir := c.cfg.DataDir
|
dataDir := c.cfg.DataDir
|
||||||
if dataDir == "" {
|
if dataDir == "" {
|
||||||
home, err := os.UserHomeDir()
|
dataDir = filepath.Join(runtimepaths.HomeDir(), "gmessages")
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not get home dir and data_dir is empty: %w", err)
|
|
||||||
}
|
|
||||||
dataDir = filepath.Join(home, ".picoclaw", "gmessages")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,16 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConfig returns the default configuration for PicoClaw.
|
// DefaultConfig returns the default configuration for PicoClaw.
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
// Determine the base path for the workspace.
|
// Determine the base path for the workspace.
|
||||||
// Priority: $PICOCLAW_HOME > ~/.picoclaw
|
// Priority: $PICOCLAW_HOME > ~/.picoclaw
|
||||||
var homePath string
|
homePath := runtimepaths.HomeDir()
|
||||||
if picoclawHome := os.Getenv("PICOCLAW_HOME"); picoclawHome != "" {
|
|
||||||
homePath = picoclawHome
|
|
||||||
} else {
|
|
||||||
userHome, _ := os.UserHomeDir()
|
|
||||||
homePath = filepath.Join(userHome, ".picoclaw")
|
|
||||||
}
|
|
||||||
workspacePath := filepath.Join(homePath, "workspace")
|
workspacePath := filepath.Join(homePath, "workspace")
|
||||||
|
|
||||||
return &Config{
|
return &Config{
|
||||||
|
|
|
||||||
43
pkg/runtimepaths/paths.go
Normal file
43
pkg/runtimepaths/paths.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package runtimepaths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func firstEnv(keys ...string) string {
|
||||||
|
for _, key := range keys {
|
||||||
|
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func HomeDir() string {
|
||||||
|
if home := firstEnv("JANE_AI_HOME", "PICOCLAW_HOME"); home != "" {
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
userHome, _ := os.UserHomeDir()
|
||||||
|
preferred := filepath.Join(userHome, ".jane-ai")
|
||||||
|
if _, err := os.Stat(preferred); err == nil {
|
||||||
|
return preferred
|
||||||
|
}
|
||||||
|
legacy := filepath.Join(userHome, ".picoclaw")
|
||||||
|
if _, err := os.Stat(legacy); err == nil {
|
||||||
|
return legacy
|
||||||
|
}
|
||||||
|
return preferred
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigPath() string {
|
||||||
|
if path := firstEnv("JANE_AI_CONFIG", "PICOCLAW_CONFIG"); path != "" {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return filepath.Join(HomeDir(), "config.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuiltinSkillsOverride() string {
|
||||||
|
return firstEnv("JANE_AI_BUILTIN_SKILLS", "PICOCLAW_BUILTIN_SKILLS")
|
||||||
|
}
|
||||||
38
pkg/runtimepaths/paths_test.go
Normal file
38
pkg/runtimepaths/paths_test.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package runtimepaths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHomeDirPrefersJaneAIByDefault(t *testing.T) {
|
||||||
|
t.Setenv("HOME", t.TempDir())
|
||||||
|
if got := HomeDir(); got != filepath.Join(os.Getenv("HOME"), ".jane-ai") {
|
||||||
|
t.Fatalf("HomeDir() = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHomeDirFallsBackToLegacyDir(t *testing.T) {
|
||||||
|
home := t.TempDir()
|
||||||
|
t.Setenv("HOME", home)
|
||||||
|
legacy := filepath.Join(home, ".picoclaw")
|
||||||
|
if err := os.MkdirAll(legacy, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := HomeDir(); got != legacy {
|
||||||
|
t.Fatalf("HomeDir() = %q, want %q", got, legacy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigPathHonorsBothEnvNames(t *testing.T) {
|
||||||
|
t.Setenv("JANE_AI_CONFIG", "/tmp/jane/config.json")
|
||||||
|
if got := ConfigPath(); got != "/tmp/jane/config.json" {
|
||||||
|
t.Fatalf("ConfigPath() = %q", got)
|
||||||
|
}
|
||||||
|
t.Setenv("JANE_AI_CONFIG", "")
|
||||||
|
t.Setenv("PICOCLAW_CONFIG", "/tmp/pico/config.json")
|
||||||
|
if got := ConfigPath(); got != "/tmp/pico/config.json" {
|
||||||
|
t.Fatalf("ConfigPath() = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"jane/pkg/config"
|
"jane/pkg/config"
|
||||||
"jane/pkg/providers"
|
"jane/pkg/providers"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
// registerSessionRoutes binds session list and detail endpoints to the ServeMux.
|
// registerSessionRoutes binds session list and detail endpoints to the ServeMux.
|
||||||
|
|
@ -248,7 +249,7 @@ func truncateRunes(s string, maxLen int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionsDir resolves the path to the gateway's session storage directory.
|
// sessionsDir resolves the path to the gateway's session storage directory.
|
||||||
// It reads the workspace from config, falling back to ~/.picoclaw/workspace.
|
// It reads the workspace from config, falling back to the resolved app home workspace.
|
||||||
func (h *Handler) sessionsDir() (string, error) {
|
func (h *Handler) sessionsDir() (string, error) {
|
||||||
cfg, err := config.LoadConfig(h.configPath)
|
cfg, err := config.LoadConfig(h.configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -257,8 +258,7 @@ func (h *Handler) sessionsDir() (string, error) {
|
||||||
|
|
||||||
workspace := cfg.Agents.Defaults.Workspace
|
workspace := cfg.Agents.Defaults.Workspace
|
||||||
if workspace == "" {
|
if workspace == "" {
|
||||||
home, _ := os.UserHomeDir()
|
workspace = filepath.Join(runtimepaths.HomeDir(), "workspace")
|
||||||
workspace = filepath.Join(home, ".picoclaw", "workspace")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand ~ prefix
|
// Expand ~ prefix
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"jane/pkg/config"
|
"jane/pkg/config"
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
"jane/pkg/skills"
|
"jane/pkg/skills"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -309,18 +310,11 @@ func loadSkillContent(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func globalConfigDir() string {
|
func globalConfigDir() string {
|
||||||
if home := os.Getenv("PICOCLAW_HOME"); home != "" {
|
return runtimepaths.HomeDir()
|
||||||
return home
|
|
||||||
}
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return filepath.Join(home, ".picoclaw")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinSkillsDir() string {
|
func builtinSkillsDir() string {
|
||||||
if path := os.Getenv("PICOCLAW_BUILTIN_SKILLS"); path != "" {
|
if path := runtimepaths.BuiltinSkillsOverride(); path != "" {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
|
|
|
||||||
|
|
@ -7,47 +7,60 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"jane/pkg/runtimepaths"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetDefaultConfigPath returns the default path to the picoclaw config file.
|
// GetDefaultConfigPath returns the default path to the picoclaw config file.
|
||||||
func GetDefaultConfigPath() string {
|
func GetDefaultConfigPath() string {
|
||||||
|
if configPath := os.Getenv("JANE_AI_CONFIG"); configPath != "" {
|
||||||
|
return configPath
|
||||||
|
}
|
||||||
if configPath := os.Getenv("PICOCLAW_CONFIG"); configPath != "" {
|
if configPath := os.Getenv("PICOCLAW_CONFIG"); configPath != "" {
|
||||||
return configPath
|
return configPath
|
||||||
}
|
}
|
||||||
if picoclawHome := os.Getenv("PICOCLAW_HOME"); picoclawHome != "" {
|
if home := os.Getenv("JANE_AI_HOME"); home != "" {
|
||||||
return filepath.Join(picoclawHome, "config.json")
|
return filepath.Join(home, "config.json")
|
||||||
}
|
}
|
||||||
home, err := os.UserHomeDir()
|
if home := os.Getenv("PICOCLAW_HOME"); home != "" {
|
||||||
if err != nil {
|
return filepath.Join(home, "config.json")
|
||||||
return "config.json"
|
|
||||||
}
|
}
|
||||||
return filepath.Join(home, ".picoclaw", "config.json")
|
return runtimepaths.ConfigPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindPicoclawBinary locates the picoclaw executable.
|
// FindPicoclawBinary locates the picoclaw executable.
|
||||||
// Search order:
|
// Search order:
|
||||||
// 1. PICOCLAW_BINARY environment variable (explicit override)
|
// 1. JANE_AI_BINARY or PICOCLAW_BINARY environment variable (explicit override)
|
||||||
// 2. Same directory as the current executable
|
// 2. Same directory as the current executable
|
||||||
// 3. Falls back to "picoclaw" and relies on $PATH
|
// 3. Falls back to "jane-ai" or "picoclaw" on $PATH
|
||||||
func FindPicoclawBinary() string {
|
func FindPicoclawBinary() string {
|
||||||
binaryName := "picoclaw"
|
if p := os.Getenv("JANE_AI_BINARY"); p != "" {
|
||||||
if runtime.GOOS == "windows" {
|
if info, _ := os.Stat(p); info != nil && !info.IsDir() {
|
||||||
binaryName = "picoclaw.exe"
|
return p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p := os.Getenv("PICOCLAW_BINARY"); p != "" {
|
if p := os.Getenv("PICOCLAW_BINARY"); p != "" {
|
||||||
if info, _ := os.Stat(p); info != nil && !info.IsDir() {
|
if info, _ := os.Stat(p); info != nil && !info.IsDir() {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binaryNames := []string{"jane-ai", "picoclaw"}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
binaryNames = []string{"jane-ai.exe", "picoclaw.exe"}
|
||||||
|
}
|
||||||
if exe, err := os.Executable(); err == nil {
|
if exe, err := os.Executable(); err == nil {
|
||||||
|
for _, binaryName := range binaryNames {
|
||||||
candidate := filepath.Join(filepath.Dir(exe), binaryName)
|
candidate := filepath.Join(filepath.Dir(exe), binaryName)
|
||||||
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
|
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
|
||||||
return candidate
|
return candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, err := exec.LookPath(binaryNames[0]); err == nil {
|
||||||
|
return path
|
||||||
|
}
|
||||||
return "picoclaw"
|
return "picoclaw"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,12 @@ export function useCredentialsPage() {
|
||||||
const data = event.data as
|
const data = event.data as
|
||||||
| { type?: string; flowId?: string; status?: string }
|
| { type?: string; flowId?: string; status?: string }
|
||||||
| undefined
|
| undefined
|
||||||
if (!data || data.type !== "picoclaw-oauth-result" || !data.flowId) {
|
if (
|
||||||
|
!data ||
|
||||||
|
(data.type !== "jane-ai-oauth-result" &&
|
||||||
|
data.type !== "picoclaw-oauth-result") ||
|
||||||
|
!data.flowId
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,20 +32,25 @@ export interface ChatMessage {
|
||||||
|
|
||||||
type ConnectionState = "disconnected" | "connecting" | "connected" | "error"
|
type ConnectionState = "disconnected" | "connecting" | "connected" | "error"
|
||||||
|
|
||||||
const LAST_SESSION_STORAGE_KEY = "picoclaw:last-session-id"
|
const LAST_SESSION_STORAGE_KEY = "jane-ai:last-session-id"
|
||||||
|
const LEGACY_SESSION_STORAGE_KEY = "picoclaw:last-session-id"
|
||||||
|
|
||||||
function readStoredSessionId(): string {
|
function readStoredSessionId(): string {
|
||||||
const value = localStorage.getItem(LAST_SESSION_STORAGE_KEY)?.trim()
|
const value =
|
||||||
|
localStorage.getItem(LAST_SESSION_STORAGE_KEY)?.trim() ||
|
||||||
|
localStorage.getItem(LEGACY_SESSION_STORAGE_KEY)?.trim()
|
||||||
return value || ""
|
return value || ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeStoredSessionId(sessionId: string) {
|
function writeStoredSessionId(sessionId: string) {
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
localStorage.setItem(LAST_SESSION_STORAGE_KEY, sessionId)
|
localStorage.setItem(LAST_SESSION_STORAGE_KEY, sessionId)
|
||||||
|
localStorage.setItem(LEGACY_SESSION_STORAGE_KEY, sessionId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.removeItem(LAST_SESSION_STORAGE_KEY)
|
localStorage.removeItem(LAST_SESSION_STORAGE_KEY)
|
||||||
|
localStorage.removeItem(LEGACY_SESSION_STORAGE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSessionId(): string {
|
function generateSessionId(): string {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue