mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
Adds packages/desktop, a small native app (Rust: tao, wry/WKWebView, muda, objc2) that opens Codeman in its own window. The universal app is about 5 MB (3.2 MB as a DMG) and runs on macOS 11.3 or later. - Connect screen with two tabs. This Mac finds the local Codeman and offers Open, Start or Install. Start uses launchd for a LaunchAgent install and codeman web -d otherwise. It never starts a second server while one runs, because two servers on one data dir share live sessions. Another machine connects by address (recent servers, Keychain logins) and can install Codeman there over SSH. - Installs run Codeman's install.sh in Terminal from a self-deleting .command script. The installer needs a terminal for sudo, Homebrew and ssh. A local install uses --local --service and the first free port from 3000, and the app opens Codeman once it answers. - The local Codeman's login is read from its LaunchAgent or ~/.codeman/.env. It is sent only to 127.0.0.1 on that port, only while the user's own Codeman process holds it, and is never saved. - Native JS dialogs (alert, confirm, prompt) and HTTP Basic challenge answers, both scoped to Codeman's own pages, so the page keeps working after a server restart. App cookies are cleared before every connect. - Pop-outs through window.CodemanHost, with an allowlist (webview/ never opens a window), external links in the default browser, downloads to ~/Downloads, real full screen and window state that is remembered. - The connect page refuses a WebKit older than Safari 16.4, which Codeman's terminal needs. Codeman side: viewSubagentTranscript shows a toast when the pop-up is refused instead of failing, with zh-CN strings and a test that pins them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
4.3 KiB
Bash
Executable File
111 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build Codeman.app (and optionally a .dmg) from packages/desktop, on macOS.
|
|
#
|
|
# packages/desktop/scripts/bundle-macos.sh # this Mac's architecture
|
|
# packages/desktop/scripts/bundle-macos.sh --universal --dmg
|
|
# SIGN_IDENTITY="Developer ID Application: ..." packages/desktop/scripts/bundle-macos.sh --dmg
|
|
#
|
|
# Without SIGN_IDENTITY the app is signed ad hoc: it runs on the Mac that built
|
|
# it, and a copy downloaded on another Mac is stopped by Gatekeeper. On macOS 15
|
|
# and later, try to open it once, then click Open Anyway in System Settings >
|
|
# Privacy & Security; on macOS 14 and earlier, right-click it and choose Open.
|
|
# Or run `xattr -dr com.apple.quarantine /Applications/Codeman.app` on the copy
|
|
# in /Applications. A Developer ID signature + notarization avoids all of this.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
[ "$(uname -s)" = Darwin ] || { echo "bundle-macos.sh runs on macOS only" >&2; exit 1; }
|
|
|
|
UNIVERSAL=0
|
|
DMG=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--universal) UNIVERSAL=1 ;;
|
|
--dmg) DMG=1 ;;
|
|
*) echo "unknown option: $arg" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
VERSION=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -1)
|
|
[ -n "$VERSION" ] || { echo "could not read the version from Cargo.toml" >&2; exit 1; }
|
|
|
|
# The binary targets the same macOS as LSMinimumSystemVersion in Info.plist,
|
|
# so the two cannot disagree.
|
|
MACOSX_DEPLOYMENT_TARGET=$(/usr/libexec/PlistBuddy -c 'Print :LSMinimumSystemVersion' macos/Info.plist)
|
|
[ -n "$MACOSX_DEPLOYMENT_TARGET" ] || { echo "could not read LSMinimumSystemVersion from macos/Info.plist" >&2; exit 1; }
|
|
export MACOSX_DEPLOYMENT_TARGET
|
|
|
|
WORK=$(mktemp -d)
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
# Build with cargo and set BUILT to the executable cargo reports for
|
|
# codeman-desktop. Asking cargo, rather than assuming ./target, keeps
|
|
# CARGO_TARGET_DIR, build.target-dir and build.target from a cargo config
|
|
# working, and a stale binary from an older build is never bundled.
|
|
cargo_build() {
|
|
local json
|
|
json=$(cargo build --locked --release "$@" --message-format=json-render-diagnostics)
|
|
BUILT=$(printf '%s\n' "$json" | awk '
|
|
index($0, "\"reason\":\"compiler-artifact\"") &&
|
|
index($0, "\"kind\":[\"bin\"]") &&
|
|
index($0, "\"name\":\"codeman-desktop\"") &&
|
|
match($0, /"executable":"[^"]*"/) { path = substr($0, RSTART + 14, RLENGTH - 15) }
|
|
END { print path }')
|
|
if [ -z "$BUILT" ] || [ ! -x "$BUILT" ]; then
|
|
echo "cargo did not report a codeman-desktop executable${BUILT:+ (it named $BUILT)}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
if [ "$UNIVERSAL" = 1 ]; then
|
|
rustup target add aarch64-apple-darwin x86_64-apple-darwin >/dev/null
|
|
cargo_build --target aarch64-apple-darwin
|
|
ARM_BIN=$BUILT
|
|
cargo_build --target x86_64-apple-darwin
|
|
INTEL_BIN=$BUILT
|
|
BIN="$WORK/codeman-desktop-universal"
|
|
lipo -create -output "$BIN" "$ARM_BIN" "$INTEL_BIN"
|
|
else
|
|
cargo_build
|
|
BIN=$BUILT
|
|
fi
|
|
|
|
APP=dist/Codeman.app
|
|
rm -rf "$APP"
|
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
|
cp "$BIN" "$APP/Contents/MacOS/codeman-desktop"
|
|
sed "s/__VERSION__/$VERSION/g" macos/Info.plist > "$APP/Contents/Info.plist"
|
|
plutil -lint "$APP/Contents/Info.plist" >/dev/null
|
|
printf 'APPL????' > "$APP/Contents/PkgInfo"
|
|
|
|
# assets/icon.png (1024x1024, already on Apple's icon grid) -> Codeman.icns
|
|
ICONSET="$WORK/Codeman.iconset"
|
|
mkdir -p "$ICONSET"
|
|
for size in 16 32 128 256 512; do
|
|
sips -z "$size" "$size" assets/icon.png --out "$ICONSET/icon_${size}x${size}.png" >/dev/null
|
|
double=$((size * 2))
|
|
sips -z "$double" "$double" assets/icon.png --out "$ICONSET/icon_${size}x${size}@2x.png" >/dev/null
|
|
done
|
|
iconutil -c icns "$ICONSET" -o "$APP/Contents/Resources/Codeman.icns"
|
|
|
|
if [ -n "${SIGN_IDENTITY:-}" ]; then
|
|
codesign --force --options runtime --timestamp \
|
|
--entitlements macos/entitlements.plist --sign "$SIGN_IDENTITY" "$APP"
|
|
else
|
|
codesign --force --sign - "$APP"
|
|
fi
|
|
codesign --verify --strict "$APP"
|
|
|
|
echo "Built $(pwd)/$APP ($(du -sh "$APP" | cut -f1), version $VERSION, macOS $MACOSX_DEPLOYMENT_TARGET or later)"
|
|
|
|
if [ "$DMG" = 1 ]; then
|
|
STAGE="$WORK/dmg"
|
|
mkdir -p "$STAGE"
|
|
cp -R "$APP" "$STAGE/"
|
|
ln -s /Applications "$STAGE/Applications"
|
|
DMG_PATH="dist/Codeman-$VERSION.dmg"
|
|
rm -f "$DMG_PATH"
|
|
hdiutil create -quiet -volname "Codeman" -srcfolder "$STAGE" -ov -format UDZO "$DMG_PATH"
|
|
echo "Built $(pwd)/$DMG_PATH ($(du -sh "$DMG_PATH" | cut -f1))"
|
|
fi
|