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
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,18 @@ docker-clean:
docker compose -f docker/docker-compose.full.yml down -v
docker rmi picoclaw:latest picoclaw:full 2>/dev/null || true


## build-macos-app: Build PicoClaw macOS .app bundle (no terminal window)
build-macos-app:
@echo "Building macOS .app bundle..."
@if [ "$(UNAME_S)" != "Darwin" ]; then \
echo "Error: This target is only available on macOS"; \
exit 1; \
fi
@cd web && $(MAKE) build && cd ..
@./scripts/build-macos-app.sh $(BINARY_NAME)-$(PLATFORM)-$(ARCH)
@echo "macOS .app bundle created: $(BUILD_DIR)/PicoClaw.app"

## help: Show this help message
help:
@echo "picoclaw Makefile"
Expand Down
28 changes: 16 additions & 12 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func init() {
FormatFieldValue: formatFieldValue,
}

logger = zerolog.New(consoleWriter).With().Timestamp().Logger()
logger = zerolog.New(consoleWriter).With().Timestamp().Caller().Logger()
fileLogger = zerolog.Logger{}
})
}
Expand Down Expand Up @@ -94,6 +94,12 @@ func SetLevel(level LogLevel) {
zerolog.SetGlobalLevel(level)
}

func SetConsoleLevel(level LogLevel) {
mu.Lock()
defer mu.Unlock()
logger = logger.Level(level)
}

func GetLevel() LogLevel {
mu.RLock()
defer mu.RUnlock()
Expand Down Expand Up @@ -134,9 +140,9 @@ func DisableFileLogging() {
fileLogger = zerolog.Logger{}
}

func getCallerInfo() (string, int, string) {
func getCallerSkip() int {
for i := 2; i < 15; i++ {
pc, file, line, ok := runtime.Caller(i)
pc, file, _, ok := runtime.Caller(i)
if !ok {
continue
}
Expand All @@ -158,10 +164,10 @@ func getCallerInfo() (string, int, string) {
continue
}

return filepath.Base(file), line, filepath.Base(funcName)
return i - 1
}

return "???", 0, "???"
return 3
}

//nolint:zerologlint
Expand All @@ -187,19 +193,16 @@ func logMessage(level LogLevel, component string, message string, fields map[str
return
}

callerFile, callerLine, callerFunc := getCallerInfo()
skip := getCallerSkip()

event := getEvent(logger, level)

// Build combined field with component and caller
if component != "" {
event.Str("caller", fmt.Sprintf("%-6s %s:%d (%s)", component, callerFile, callerLine, callerFunc))
} else {
event.Str("caller", fmt.Sprintf("<none> %s:%d (%s)", callerFile, callerLine, callerFunc))
event.Str("component", component)
}

appendFields(event, fields)
event.Msg(message)
event.CallerSkipFrame(skip).Msg(message)

// Also log to file if enabled
if fileLogger.GetLevel() != zerolog.NoLevel {
Expand All @@ -208,9 +211,10 @@ func logMessage(level LogLevel, component string, message string, fields map[str
if component != "" {
fileEvent.Str("component", component)
}
// fileEvent.Str("caller", fmt.Sprintf("%s:%d (%s)", callerFile, callerLine, callerFunc))

appendFields(fileEvent, fields)
fileEvent.Msg(message)
fileEvent.CallerSkipFrame(skip).Msg(message)
}

if level == FATAL {
Expand Down
108 changes: 108 additions & 0 deletions scripts/build-macos-app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/bin/bash
# Build macOS .app bundle for PicoClaw Launcher

set -e

EXECUTABLE=$1

if [ -z "$EXECUTABLE" ]; then
echo "Usage: $0 <executable>"
exit 1
fi

echo "executable: $EXECUTABLE"

APP_NAME="PicoClaw Launcher"
APP_PATH="./build/${APP_NAME}.app"
APP_CONTENTS="${APP_PATH}/Contents"
APP_MACOS="${APP_CONTENTS}/MacOS"
APP_RESOURCES="${APP_CONTENTS}/Resources"
APP_EXECUTABLE="picoclaw-launcher"
ICON_SOURCE="./scripts/icon.icns"

# Clean up existing .app
if [ -d "$APP_PATH" ]; then
echo "Removing existing ${APP_PATH}"
rm -rf "$APP_PATH"
fi

# Create directory structure
echo "Creating .app bundle structure..."
mkdir -p "$APP_MACOS"
mkdir -p "$APP_RESOURCES"

# Copy executable
echo "Copying executable..."
if [ -f "./web/build/${APP_EXECUTABLE}" ]; then
cp "./web/build/${APP_EXECUTABLE}" "${APP_MACOS}/"
else
echo "Error: ./web/build/${APP_EXECUTABLE} not found. Please build the web backend first."
echo "Run: make build in web dir"
exit 1
fi
if [ -f "./build/picoclaw" ]; then
cp "./build/picoclaw" "${APP_MACOS}/"
else
echo "Error: ./build/picoclaw not found. Please build the main file first."
echo "Run: make build"
exit 1
fi
chmod +x "${APP_MACOS}/"*

# Create Info.plist
echo "Creating Info.plist..."
cat > "${APP_CONTENTS}/Info.plist" << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>picoclaw-launcher</string>
<key>CFBundleIdentifier</key>
<string>com.picoclaw.launcher</string>
<key>CFBundleName</key>
<string>PicoClaw Launcher</string>
<key>CFBundleDisplayName</key>
<string>PicoClaw Launcher</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>LSUIElement</key>
<string>1</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
EOF

#sips -z 128 128 "$ICON_SOURCE" --out "${ICONSET_PATH}/icon_128x128.png" > /dev/null 2>&1
#
## Create icns file
#iconutil -c icns "$ICONSET_PATH" -o "$ICON_OUTPUT" 2>/dev/null || {
# echo "Warning: iconutil failed"
#}

cp $ICON_SOURCE "${APP_RESOURCES}/icon.icns"

echo ""
echo "=========================================="
echo "Successfully created: ${APP_PATH}"
echo "=========================================="
echo ""
echo "To launch PicoClaw:"
echo " 1. Double-click ${APP_NAME}.app in Finder"
echo " 2. Or use: open ${APP_PATH}"
echo ""
echo "Note: The app will run in the menu bar (systray) without a terminal window."
echo ""
Binary file added scripts/icon.icns
Binary file not shown.
65 changes: 65 additions & 0 deletions scripts/setup.iss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "PicoClaw Launcher"
#define MyAppVersion "1.0"
#define MyAppPublisher "PicoClaw"
#define MyAppURL "https://github.com/sipeed/picoclaw"
#define MyAppExeName "picoclaw-launcher.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{C8A1B4E7-D5F9-4C2A-8A6E-5F4D3C2A1B0E}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\PicoClaw
DefaultGroupName={#MyAppName}
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
; on anything but x64 and Windows 11 on Arm.
ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
; meaning it should use the native 64-bit Program Files directory and
; the 64-bit view of the registry.
ArchitecturesInstallIn64BitMode=x64compatible
DisableProgramGroupPage=yes
; Remove the following line to run in administrative install mode (install for all users.)
PrivilegesRequired=lowest
OutputDir=build
OutputBaseFilename=PicoClawSetup
Compression=lzma
SolidCompression=yes
WizardStyle=modern
; SourceDir=windows
SetupIconFile=icon.ico

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Dirs]

[Files]
Source: "..\web\build\picoclaw-launcher.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}"; Flags: ignoreversion
Source: "..\build\picoclaw.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\web\backend\icon.ico"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[UninstallDelete]

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; IconFilename: "{app}\icon.ico"
Name: "{group}\Uninstall {#MyAppName}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Tasks: desktopicon; IconFilename: "{app}\icon.ico"

[Run]
Filename:"{app}\{#MyAppExeName}"; WorkingDir: "{app}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

9 changes: 6 additions & 3 deletions web/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ GO?=CGO_ENABLED=0 go
WEB_GO?=$(GO)
GOFLAGS?=-v -tags stdjson

# Build variables
BUILD_DIR=build

# Version
VERSION?=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
GIT_COMMIT=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "dev")
Expand Down Expand Up @@ -57,7 +60,7 @@ endif

# Run both frontend and backend dev servers
dev:
@if [ ! -f backend/picoclaw-web ] || [ ! -d backend/dist ]; then \
@if [ ! -f $(BUILD_DIR)/picoclaw-launcher ] || [ ! -d backend/dist ]; then \
echo "Build artifacts not found, building..."; \
$(MAKE) build; \
fi
Expand All @@ -75,7 +78,7 @@ dev-backend:
# Build frontend and embed into Go binary
build:
cd frontend && pnpm build:backend
cd backend && ${WEB_GO} build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o picoclaw-web .
${WEB_GO} build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BUILD_DIR)/picoclaw-launcher ./backend/

# Run all tests
test:
Expand All @@ -89,5 +92,5 @@ lint:

# Clean build artifacts
clean:
rm -rf frontend/dist backend/dist backend/picoclaw-web
rm -rf frontend/dist backend/dist $(BUILD_DIR)/*
mkdir -p backend/dist && touch backend/dist/.gitkeep
Loading
Loading