Skip to content

Commit f9b3b0b

Browse files
matthew-pilotmatthew-pilotTeoSlayer
authored
feat(pilotctl): add pilotctl update command (PILOT-396) (#262)
* feat(pilotctl): add update command for one-shot updater invoke (PILOT-396) pilotctl update runs the updater once — checks the latest release, downloads and installs if newer. When the daemon is not running (manual mode), it also re-runs skill install so newly installed binaries have matching skill definitions. Dependency: github.com/pilot-protocol/updater now exposes RunOnce() for one-shot invocation without a periodic loop. Closes PILOT-396 * chore: regenerate docs/cli-reference.md via gen-cli-reference.sh PILOT-396 * fix: remove MOTD line from cli-reference.md (CI env has no daemon) --------- Co-authored-by: matthew-pilot <matthew@pilotprotocol.network> Co-authored-by: Calin Teodor <t.calin@student.vu.nl>
1 parent 08ea337 commit f9b3b0b

5 files changed

Lines changed: 93 additions & 1 deletion

File tree

cmd/pilotctl/main.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,16 @@ Common keys:
11281128
"version": `Usage: pilotctl version
11291129
11301130
Print the pilotctl build version string.
1131+
`,
1132+
"update": `Usage: pilotctl update [flags]
1133+
1134+
Run the updater once — check for new releases and install if available.
1135+
In manual mode (daemon not running), re-runs skill install so newly
1136+
installed binaries have matching skill definitions.
1137+
1138+
Flags:
1139+
--repo <name> GitHub owner/repo for releases (default: TeoSlayer/pilotprotocol)
1140+
--pin <tag> pin to a specific release tag (e.g. v1.10.5)
11311141
`,
11321142
"updates": `Usage: pilotctl updates [flags]
11331143
@@ -1315,6 +1325,7 @@ Diagnostic commands:
13151325
pilotctl bench <address|hostname> [size_mb] [--timeout <dur>]
13161326
pilotctl listen <port> [--count <n>] [--timeout <dur>]
13171327
pilotctl broadcast <network_id> <message>
1328+
pilotctl update [--pin <tag>] run the updater once — check and install new release
13181329
pilotctl updates [--count <n>] [--scope <scope>] read https://teoslayer.github.io/pilot-changelog/feed.xml
13191330
13201331
Agent tool discovery:
@@ -1412,6 +1423,10 @@ dispatch:
14121423
cmdQuickstart()
14131424
return
14141425

1426+
case "update":
1427+
cmdUpdate(cmdArgs)
1428+
return
1429+
14151430
case "updates":
14161431
cmdUpdates(cmdArgs)
14171432
return

cmd/pilotctl/updates.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import (
1111
"path/filepath"
1212
"strings"
1313
"time"
14+
15+
"github.com/pilot-protocol/common/driver"
16+
"github.com/pilot-protocol/skillinject"
17+
"github.com/pilot-protocol/updater"
1418
)
1519

1620
// changelogFeedURL is the canonical RSS 2.0 feed for the public Pilot
@@ -204,6 +208,74 @@ func collapseWhitespace(s string) string {
204208
return strings.TrimSpace(b.String())
205209
}
206210

211+
// cmdUpdate runs the updater once — checking for new releases, downloading and
212+
// installing if available. When the daemon is not running (manual mode), it
213+
// also re-runs skill install so newly installed binaries have matching skills.
214+
//
215+
// Flags:
216+
// --repo <name> : GitHub owner/repo for releases (default: TeoSlayer/pilotprotocol)
217+
// --pin <tag> : pin to a specific release tag (e.g. v1.10.5)
218+
// (global) --json : emit machine-readable JSON
219+
func cmdUpdate(args []string) {
220+
flags, _ := parseFlags(args)
221+
repo := flagString(flags, "repo", "TeoSlayer/pilotprotocol")
222+
pin := flagString(flags, "pin", "")
223+
224+
// Determine install directory: where the updater binary lives.
225+
updaterBin, err := findCompanionBinary("pilot-updater", "PILOT_UPDATER_BIN")
226+
if err != nil {
227+
fatalCode("internal", "cannot locate pilot-updater binary: %v", err)
228+
}
229+
installDir := filepath.Dir(updaterBin)
230+
231+
u := updater.New(updater.Config{
232+
CheckInterval: 0, // unused for RunOnce
233+
Repo: repo,
234+
InstallDir: installDir,
235+
Version: version,
236+
PinnedVersion: pin,
237+
})
238+
239+
u.RunOnce()
240+
241+
if jsonOutput {
242+
outputOK(map[string]interface{}{
243+
"install_dir": installDir,
244+
"repo": repo,
245+
"pinned": pin != "",
246+
})
247+
return
248+
}
249+
fmt.Printf("Update check complete. Install dir: %s\n", installDir)
250+
251+
// In manual mode (no daemon running), re-run skill install so skills
252+
// match the (possibly updated) binaries.
253+
if !daemonRunning() {
254+
report, err := runTick()
255+
if err != nil {
256+
fmt.Fprintf(os.Stderr, "warning: skill install failed: %v\n", err)
257+
} else {
258+
c := report.Counts()
259+
fmt.Printf("Skills: %d files checked (%d up-to-date, %d installed, %d errors)\n",
260+
len(report.Outcomes),
261+
c[skillinject.ActionNoop],
262+
c[skillinject.ActionCreate]+c[skillinject.ActionRewrite],
263+
c[skillinject.ActionError])
264+
}
265+
}
266+
}
267+
268+
// daemonRunning checks whether the daemon socket is reachable.
269+
func daemonRunning() bool {
270+
sock := getSocket()
271+
d, err := driver.Connect(sock)
272+
if err != nil {
273+
return false
274+
}
275+
d.Close()
276+
return true
277+
}
278+
207279
// fetchChangelogFeed returns the cached feed body if it's fresh (< 5 min)
208280
// and `refresh` is false; otherwise hits the network. Returns
209281
// (body, fromCache, err). Cache lives at ~/.pilot/updates-cache.xml so

docs/cli-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Diagnostic commands:
7676
pilotctl bench <address|hostname> [size_mb] [--timeout <dur>]
7777
pilotctl listen <port> [--count <n>] [--timeout <dur>]
7878
pilotctl broadcast <network_id> <message>
79+
pilotctl update [--pin <tag>] run the updater once — check and install new release
7980
pilotctl updates [--count <n>] [--scope <scope>] read https://teoslayer.github.io/pilot-changelog/feed.xml
8081
8182
Agent tool discovery:

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/pilot-protocol/runtime v0.3.1
1717
github.com/pilot-protocol/skillinject v0.2.2
1818
github.com/pilot-protocol/trustedagents v0.2.3
19-
github.com/pilot-protocol/updater v0.2.2-0.20260529065627-220ed5b8383f
19+
github.com/pilot-protocol/updater v0.2.2-0.20260616131353-92a3a30a235e
2020
github.com/pilot-protocol/webhook v0.2.0
2121
golang.org/x/sys v0.45.0
2222
)
@@ -25,3 +25,5 @@ require (
2525
github.com/expr-lang/expr v1.17.8 // indirect
2626
golang.org/x/net v0.55.0 // indirect
2727
)
28+
29+
replace github.com/pilot-protocol/updater v0.2.2-0.20260616131353-92a3a30a235e => github.com/pilot-protocol/updater v0.2.2-0.20260616131353-92a3a30a235e

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ github.com/pilot-protocol/trustedagents v0.2.3 h1:QQJHYqzPrECJwkCev0xIDBMjd92uht
5050
github.com/pilot-protocol/trustedagents v0.2.3/go.mod h1:gDgEOC9lHmXSS9v45h80XxlmUS861soIrA0AsbXiSV4=
5151
github.com/pilot-protocol/updater v0.2.2-0.20260529065627-220ed5b8383f h1:1dyunPeEOriqTv1xbt0fuDwieA5V5NLU1nVSC2714jU=
5252
github.com/pilot-protocol/updater v0.2.2-0.20260529065627-220ed5b8383f/go.mod h1:/I0uhVk1SljAOEYmjTdI/6CP7UmemmV4WB22ai1FxUw=
53+
github.com/pilot-protocol/updater v0.2.2-0.20260616131353-92a3a30a235e h1:vFzuw5dUVi0igwI2PdVzDY8OnY6FDLzM05wzI75zUZ8=
54+
github.com/pilot-protocol/updater v0.2.2-0.20260616131353-92a3a30a235e/go.mod h1:/I0uhVk1SljAOEYmjTdI/6CP7UmemmV4WB22ai1FxUw=
5355
github.com/pilot-protocol/webhook v0.2.0 h1:3UFU9X2yBb0iKlPbzVcism+Z6yCrBBaOgdo9+vd4Wf4=
5456
github.com/pilot-protocol/webhook v0.2.0/go.mod h1:WVXhHFg+o0pHHk+4nXMCh1zl/ZAyZ3AXrtx6mNuZS6g=
5557
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=

0 commit comments

Comments
 (0)