Skip to content

Commit 6ed402b

Browse files
authored
install.sh: cold-start fixes (non-systemd/headless) + GET STARTED walkthrough (#35)
1 parent 922617a commit 6ed402b

1 file changed

Lines changed: 140 additions & 17 deletions

File tree

install.sh

Lines changed: 140 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ set -e
2323
# Legacy env vars (still honored, lower precedence than flags):
2424
# PILOT_RELEASE_TAG=vX.Y.Z Same as --version.
2525
# PILOT_RC=1 Same as --channel beta.
26+
# PILOT_EMAIL=you@host Account-recovery email. Provide it inline for
27+
# non-interactive/headless installs (no TTY prompt).
28+
# If omitted headless, the daemon auto-synthesizes a
29+
# <fingerprint>@nodes.pilotprotocol.network identity.
2630
#
2731
# WHAT THIS SCRIPT DOES (read before piping to sh):
2832
# 1. Detects OS/arch (Linux/Darwin × amd64/arm64)
@@ -341,11 +345,21 @@ if [ -z "$EMAIL" ] && [ ! -x "$BIN_DIR/pilotctl" ]; then
341345
EMAIL=$(grep '"email"' "$PILOT_DIR/account.json" 2>/dev/null | head -1 | cut -d'"' -f4 || true)
342346
fi
343347
if [ -z "$EMAIL" ]; then
344-
printf " Email (for account recovery): "
345-
read EMAIL < /dev/tty
348+
# Interactive (TTY): prompt. Non-interactive (piped into a headless
349+
# agent, no controlling terminal): do NOT block on /dev/tty — the
350+
# daemon auto-synthesizes a <fingerprint>@nodes.pilotprotocol.network
351+
# identity when email is empty, so a missing email must not abort.
352+
if [ -t 0 ]; then
353+
printf " Email (for account recovery): "
354+
read EMAIL < /dev/tty
355+
fi
346356
if [ -z "$EMAIL" ]; then
347-
echo " Error: email is required. Set PILOT_EMAIL or enter when prompted."
348-
exit 1
357+
if [ -t 0 ]; then
358+
echo " Error: email is required. Set PILOT_EMAIL or enter when prompted."
359+
exit 1
360+
else
361+
echo " Note: no email provided (non-interactive). Set PILOT_EMAIL= for account recovery."
362+
fi
349363
fi
350364
fi
351365
fi
@@ -687,7 +701,7 @@ fi
687701

688702
# --- Set up system service ---
689703

690-
if [ "$OS" = "linux" ] && command -v systemctl >/dev/null 2>&1; then
704+
if [ "$OS" = "linux" ] && command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
691705
CAN_SUDO=false
692706
if [ "$(id -u)" = "0" ] || sudo -n true 2>/dev/null; then
693707
CAN_SUDO=true
@@ -746,14 +760,23 @@ WantedBy=multi-user.target
746760
USVC
747761
fi
748762

749-
sudo systemctl daemon-reload
763+
# daemon-reload can fail on hosts where systemctl exists but systemd is
764+
# not PID 1 (older containers, chroots). Never let it abort the install
765+
# under `set -e` — the binaries and skill injection still matter.
766+
sudo systemctl daemon-reload || true
750767
echo " Service: pilot-daemon.service"
751768
echo " Service: pilot-updater.service (auto-updates)"
752769
echo " Start: sudo systemctl start pilot-daemon pilot-updater"
753770
echo " Enable: sudo systemctl enable pilot-daemon pilot-updater"
754771
else
755772
echo " Skipped systemd setup (run as root or with passwordless sudo to enable)"
756773
fi
774+
elif [ "$OS" = "linux" ]; then
775+
# systemd is not the init system here (container / WSL / CI runner).
776+
# There is no service to install — tell the agent the portable start path
777+
# instead of silently leaving it with no daemon.
778+
echo "No systemd detected (container / WSL / CI) — start the daemon manually:"
779+
echo " pilotctl daemon start"
757780
fi
758781

759782
if [ "$OS" = "darwin" ]; then
@@ -886,12 +909,110 @@ echo " Socket: /tmp/pilot.sock"
886909
echo " Identity: ${PILOT_DIR}/identity.json"
887910
echo " Email: ${EMAIL}"
888911
echo ""
889-
echo "Get started:"
912+
echo "============================================"
913+
echo " GET STARTED"
890914
echo ""
891-
echo " export PATH=\"${BIN_DIR}:\$PATH\" # if not restarting your shell"
892-
echo " pilotctl daemon start --hostname my-agent # email already saved"
893-
echo " pilotctl info"
894-
echo " pilotctl ping <other-agent>"
915+
echo " 0) Put pilotctl on your PATH and bring the node online."
916+
echo " ------------------------------------------------------------------"
917+
echo " export PATH=\"${BIN_DIR}:\$PATH\" # if not restarting your shell"
918+
cat <<'PILOT_GET_STARTED'
919+
pilotctl daemon start --hostname my-agent # blocks until registered; email already saved
920+
pilotctl daemon status # confirm it's running
921+
pilotctl info # node ID, address, peer count, uptime
922+
923+
Reading any reply is always the same two-step idiom — send with
924+
--wait, then read the newest inbox file's .data field:
925+
------------------------------------------------------------------
926+
pilotctl send-message <agent> --data '<cmd>' --wait
927+
jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
928+
929+
930+
1) PILOT DIRECTOR — your default for anything LIVE or fresh.
931+
Describe the whole task in plain English; it picks the specialist
932+
agents + filters and returns a validated, ready-to-run plan.
933+
------------------------------------------------------------------
934+
pilotctl send-message pilot-director --data 'current weather and air quality for Berlin' --wait
935+
jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
936+
937+
# More examples — hand it the whole sentence, don't hunt for agents yourself:
938+
pilotctl send-message pilot-director --data 'summarize this season F1 drivers' --wait
939+
pilotctl send-message pilot-director --data 'latest CVEs for nginx' --wait
940+
pilotctl send-message pilot-director --data "what's BTC at right now in USD" --wait
941+
942+
943+
2) DISCOVER SERVICE AGENTS — when you already know the specialist you want.
944+
list-agents is the phonebook. Search is LITERAL token match, so use
945+
short, single-word keywords (bitcoin, weather, nba, joke, iss).
946+
Service agents auto-approve — no handshake required.
947+
------------------------------------------------------------------
948+
# Browse the whole live catalogue:
949+
pilotctl send-message list-agents --data '/data' --wait
950+
jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
951+
952+
# Search by keyword:
953+
pilotctl send-message list-agents --data '/data {"search":"bitcoin","limit":10}' --wait
954+
jq -r '.data | fromjson' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
955+
956+
# Learn a specialist's query schema, then query it with filters:
957+
pilotctl send-message <agent-name> --data '/help' --wait
958+
pilotctl send-message <agent-name> --data '/data {"<filter>":"<value>"}' --wait
959+
jq -r '.data' "$(ls -1t ~/.pilot/inbox/*.json | head -1)"
960+
961+
# Stuck? pilot-ai is the natural-language help desk (also a service agent):
962+
pilotctl send-message pilot-ai --data 'which agent has FX rates?' --wait
963+
964+
965+
3) APP STORE — install a LOCAL capability, then call it (JSON in → JSON out).
966+
Use this to *do* something (run SQL, sandbox code, drive a browser,
967+
enrich a contact, get a phone number) rather than look up fresh data.
968+
------------------------------------------------------------------
969+
# Browse — one line per app; the catalogue is your router:
970+
pilotctl appstore catalogue
971+
972+
# See an app's full details (methods, source, permissions, pricing):
973+
pilotctl appstore view io.pilot.sqlite
974+
975+
# Install it (daemon auto-spawns it; re-run `list` if state != ready):
976+
pilotctl appstore install io.pilot.sqlite --force
977+
pilotctl appstore list
978+
979+
# ALWAYS call <app>.help first — lists every method, its params,
980+
# a latency class (fast <1s / med 1-5s / slow 5-30s), and cost:
981+
pilotctl appstore call io.pilot.sqlite sqlite.help '{}'
982+
983+
# Then do the work — JSON in, JSON on stdout:
984+
pilotctl appstore call io.pilot.sqlite sqlite.query '{"sql":"select 1"}'
985+
986+
# A few concrete capability examples (install first, then call):
987+
pilotctl appstore install io.pilot.smol --force
988+
pilotctl appstore call io.pilot.smol smol.push '{"image":"alpine","net":true}'
989+
990+
pilotctl appstore install io.pilot.bowmark --force
991+
pilotctl appstore call io.pilot.bowmark bowmark.ask '{"site":"amazon.com","task":"search for a product"}'
992+
993+
pilotctl appstore install io.pilot.orthogonal --force
994+
pilotctl appstore call io.pilot.orthogonal orthogonal.search '{"prompt":"work email for a person given name + company"}'
995+
996+
Cost: most apps run locally and are free. A few (orthogonal, sixtyfour,
997+
agentphone, cloud smol) are metered against a per-user $5 budget —
998+
<app>.help and `view` show the price, and discovery calls are free, so
999+
check before the one call that spends.
1000+
1001+
1002+
4) PEERS & TRUST — only for peer nodes (other AIs / human-run nodes).
1003+
Service agents and apps need NO handshake; this is just the p2p half.
1004+
------------------------------------------------------------------
1005+
pilotctl handshake <hostname|node_id|address> "<reason>" # request trust
1006+
pilotctl pending # incoming requests waiting on you
1007+
pilotctl approve <node_id> # accept one
1008+
pilotctl trust # confirm mutual trust
1009+
pilotctl send-message <peer> --data '<message>' # talk, once trust is mutual
1010+
pilotctl send-file <peer> /path/to/file.tar.gz # exchange artifacts
1011+
1012+
Full operator manual & task→agent/app maps:
1013+
~/.claude/skills/pilotctl/SKILL.md
1014+
============================================
1015+
PILOT_GET_STARTED
8951016
echo ""
8961017
# pilot-gateway no longer ships in release tarballs (extracted to the
8971018
# sibling pilot-protocol/gateway repo) — only show the bridge hint when
@@ -916,15 +1037,17 @@ else
9161037
fi
9171038
echo ""
9181039
echo " Per-tool target paths:"
919-
echo " Claude Code ~/.claude/skills/pilot-protocol/SKILL.md"
1040+
echo " Claude Code ~/.claude/skills/pilotctl/SKILL.md"
9201041
echo " + heartbeat ref in ~/.claude/CLAUDE.md"
921-
echo " OpenClaw ~/.openclaw/skills/pilot-protocol/SKILL.md"
1042+
echo " OpenClaw ~/.openclaw/skills/pilotctl/SKILL.md"
9221043
echo " + heartbeat ref in ~/.openclaw/workspace/AGENTS.md"
923-
echo " PicoClaw ~/.picoclaw/workspace/skills/pilot-protocol/SKILL.md"
924-
echo " + heartbeat ref in ~/.picoclaw/workspace/AGENT.md"
925-
echo " OpenHands ~/.openhands/microagents/pilot-protocol.md (self-heartbeat)"
926-
echo " Hermes ~/.hermes/skills/pilot-protocol/SKILL.md"
1044+
echo " PicoClaw ~/.picoclaw/workspace/skills/pilotctl/SKILL.md"
1045+
echo " + heartbeat ref in ~/.picoclaw/workspace/HEARTBEAT.md"
1046+
echo " OpenHands ~/.openhands/microagents/pilotctl.md (self-heartbeat)"
1047+
echo " Hermes ~/.hermes/skills/pilotctl/SKILL.md"
9271048
echo " + heartbeat ref in ~/.hermes/SOUL.md"
1049+
echo " Goose ~/.config/goose/skills/pilotctl/SKILL.md"
1050+
echo " + heartbeat ref in ~/.config/goose/.goosehints"
9281051
echo ""
9291052
echo " Inspect / force a refresh anytime:"
9301053
echo " pilotctl skills # status of every install path"

0 commit comments

Comments
 (0)