* fix(skills): keep ClawHub search results on the source the operator picked ClawHub search returns each result's origin under `install.reference`, but the response model expected a flat `installRef`. That field is never present, so every row fell through to a synthesized `@owner/slug` reference. External skills.sh results were rewritten onto a ClawHub-native identity, dropping both the commit-pinned source and the "not scanned by ClawHub" trust record. Map the search wire shape explicitly and make the search contract action-specific: `installRef` always names the result's own source, `detailRef` appears only while ClawHub can serve a detail card for that identity, and `trustState` travels with unscanned sources. Clients render install directly when detail is absent instead of offering a review the Gateway must refuse. Covers the Control UI, macOS, iOS Settings, iOS AgentPro, and Android, which previously routed every row through review and could not install an external skill at all. * fix(skills): make install-only sources explicit and keep legacy review intact Address review findings on the search identity contract: - Replace the detail-reference capability with an explicit `installOnly` flag. A Gateway released before this field omits it, and reading omission as install-only made ordinary registry results skip the reviewed-version flow on every client. Absence now means the existing review-then-install path. - Parse closed source variants in the producer. A row whose source is unknown, whose external reference is missing, or whose registry publisher is absent is dropped instead of falling through to `@owner/slug`, which was the original source swap in a different disguise. - Carry the exact install reference alongside the canonical slug. The Gateway already records `requestedReference`; the clients dropped it and matched installs by slug, so a completed external install read back as unknown. - Gate the direct-install action on admin rights. The row previously stayed enabled for read-only operators and reached a guard that silently returned. - Route the unscanned-source warning through the native and Control UI string catalogs instead of a hardcoded literal. * chore(i18n): leave generated native locale artifacts to the refresh workflow Preflight isolates generated locale output from source changes: only the native sources and apps/.i18n/native-source.json belong in a feature commit. * fix(skills): satisfy Android ktlint wrapping and Swift test link construction Extract the ClawHub result action guard into a named value so the multiline condition follows ktlint wrapping, and pass the new requestedReference field in the OpenClawKit installed-link fixtures. * fix(skills): preserve external install identity across clients * test(skills): add exact refs to recommendation fixtures --------- Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
OpenClaw macOS app (dev + signing)
Quick dev run
# from repo root
scripts/restart-mac.sh
Options:
scripts/restart-mac.sh --no-sign # fastest dev; ad-hoc signing (TCC permissions do not stick)
scripts/restart-mac.sh --sign # force code signing (requires cert)
scripts/restart-mac.sh --background-only # keep services running without automatic windows
--background-only suppresses first-run onboarding, update and CLI prompts, and
the --chat/--dashboard auto-open helpers. Pairing, control-channel, and Mac
node services still start. It also keeps GUI-owned onboarding and saved Gateway
profile Keychain state cold, so a signer or ACL transition cannot raise a
SecurityAgent prompt during unattended work. The primary Gateway route still
comes from the normal environment/config endpoint. Combine it with
--attach-only when an external process owns the local Gateway.
App profiles
Launch a fully isolated app instance with the same profile name used by the CLI:
OPENCLAW_PROFILE=work /Applications/OpenClaw.app/Contents/MacOS/OpenClaw
Profile names use 1–64 lowercase letters, numbers, underscores, or hyphens and
must start with a letter or number. default selects the normal app; gateway,
mac, and node are reserved LaunchAgent identities.
scripts/restart-mac.sh intentionally rejects named profiles because its
packaging cleanup is host-global. Build/package normally, then launch the named
profile directly with the command above.
A named profile keeps state in ~/.openclaw-<name>, uses its own app defaults,
Keychain services, duplicate-instance lock, and the CLI-managed Gateway service
ai.openclaw.<name>. Unless config or environment selects a port, each profile
derives a stable port in the profile 20000...59999 range. The app does not
install or modify the host-global Mac node
service or OpenClaw login item while a profile is active. The runtime child node
still runs in process as usual. App relocation, Sparkle updates, and post-update
service repair are disabled in profile mode; update the installed app through
the normal default-profile workflow.
Packaging flows
Development bundle (signed but not notarized):
scripts/package-mac-app.sh
This creates dist/OpenClaw.app and signs it via scripts/codesign-mac-app.sh.
It is not a distribution artifact. For a notarized app ZIP and DMG, use:
scripts/package-mac-dist.sh
For an unattended Peekaboo elevation host, use the closed Foundation signing
profile and source-addressed ZIP workflow. package is an internal release
operator command: it requires the OpenClaw Foundation signing identity and
notarization credentials, and its archive is not a general-download artifact.
scripts/mac-elevation-host.sh package
cd dist/elevation-host
shasum -a 256 -c "OpenClaw-<full-source-sha>-stable.zip.sha256"
shasum -a 256 -c "OpenClaw-<full-source-sha>-stable-installer.sh.sha256"
./OpenClaw-<full-source-sha>-stable-installer.sh install \
--archive "OpenClaw-<full-source-sha>-stable.zip"
./OpenClaw-<full-source-sha>-stable-installer.sh status
The elevation package is ZIP-only, notarized and stapled, contains exactly
OpenClaw.app, omits Apple Events entitlements, records an immutable receipt,
and verifies a freshly extracted copy. The same source-addressed artifact set
includes an executable installer copied from that exact Git commit plus separate
archive and installer checksum files, so a target Mac does not need a source
checkout. Transfer the complete set and verify both checksums before running the
installer. Installation owns the separate
ai.openclaw.mac.elevation-host launchd job with RunAtLoad and KeepAlive.
It refuses to replace or race the ordinary ai.openclaw.mac Launch at login
job. recover restores the recorded prior bundle after a failed cutover;
uninstall removes only the elevation job and preserves the app, state,
Keychain, TCC, and recovery receipt. Installation exits successfully once the
launchd-owned process is Bridge-ready; missing TCC remains a degraded status
result until the required grants are present.
Signing behavior
Auto-selects identity (first match):
- Developer ID Application
- Apple Distribution
- Apple Development
- first available identity
If none found:
- errors by default
- set
ALLOW_ADHOC_SIGNING=1orSIGN_IDENTITY="-"to ad-hoc sign
Team ID audit (Sparkle mismatch guard)
After signing, we read the app bundle Team ID and compare every Mach-O inside the app. If any embedded binary has a different Team ID, signing fails.
Skip the audit:
SKIP_TEAM_ID_CHECK=1 scripts/package-mac-app.sh
Library validation workaround (dev only)
If Sparkle Team ID mismatch blocks loading (common with Apple Development certs), opt in:
DISABLE_LIBRARY_VALIDATION=1 scripts/package-mac-app.sh
This adds com.apple.security.cs.disable-library-validation to app entitlements.
Use for local dev only; keep off for release builds.
Useful env flags
SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"ALLOW_ADHOC_SIGNING=1(ad-hoc, TCC permissions do not persist)CODESIGN_TIMESTAMP=off(offline debug)DISABLE_LIBRARY_VALIDATION=1(dev-only Sparkle workaround)SKIP_TEAM_ID_CHECK=1(bypass audit)