mcptask-rails-runner 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +45 -11
- data/lib/mcptask_rails_runner/binary.rb +106 -4
- data/lib/mcptask_rails_runner/version.rb +10 -3
- data/lib/tasks/mcptask_runner.rake +9 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35e95dc7bc0beff3bde29932e758fb3f32d74481e6a3c4efdbab717f63466056
|
|
4
|
+
data.tar.gz: f96698da2f9ebad301013481638f6fd02998abf7fb8c6cf29fcf463925d6f7ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23066150e19f4c102a3177992af6646ffc19d4080cf039713954dd0892f70fb9775dcee03ce3b8f7b9b063fee6e4312f127d12602814373a3b4a4dbb5ee58610
|
|
7
|
+
data.tar.gz: 45c0ca05927c3b83150a6b6c52338030d05ab73a3d3dfdc6a32a19461e7ff62b63a8e59cef2a4b2a75dd48acb3d9a98b6dc532ae312ab01bf046a4390bab320b
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ entries below describe wrapper changes only. Binary changes are listed in the
|
|
|
5
5
|
[mcptask-releases](https://github.com/jchsoft/mcptask-releases/releases)
|
|
6
6
|
release notes for the same tag.
|
|
7
7
|
|
|
8
|
+
## 0.3.0
|
|
9
|
+
|
|
10
|
+
- The Go runner is the runner: this gem is the Rails distribution, and the
|
|
11
|
+
legacy `mcptask_runner` gem is retired to critical fixes and its role as the
|
|
12
|
+
conformance suite's reference implementation.
|
|
13
|
+
|
|
14
|
+
- The ARM Linux platform gem is named `aarch64-linux`, which is the platform
|
|
15
|
+
such hosts actually resolve. 0.2.4 published it as `arm64-linux`, which
|
|
16
|
+
matches nothing: every Graviton server, ARM CI runner and Docker container on
|
|
17
|
+
Apple Silicon fell back to the binary-less `ruby` gem and failed at run time.
|
|
18
|
+
That gem stays published and unreachable.
|
|
19
|
+
- `mcptask_runner:install` and `:update` copy the binary out of the gem to
|
|
20
|
+
`~/.mcptask/bin/mcptask_runner` and run it from there, so the scheduled job
|
|
21
|
+
they generate survives the next `bundle update` — which deletes the gem
|
|
22
|
+
directory the job used to point into. They print which version they wrote and
|
|
23
|
+
never replace a newer installed binary with an older one. Migrating hosts
|
|
24
|
+
should re-run install once, with `FORCE=1`, to regenerate the job.
|
|
25
|
+
|
|
8
26
|
## 0.2.4
|
|
9
27
|
|
|
10
28
|
- First release. Thin Rails wrapper around the `mcptask_runner` Go binary,
|
data/README.md
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
# mcptask-rails-runner
|
|
2
2
|
|
|
3
3
|
Rails distribution of the [mcptask.online](https://mcptask.online) autonomous
|
|
4
|
-
runner. A thin Ruby wrapper around the
|
|
5
|
-
ships the platform-specific binary inside
|
|
6
|
-
|
|
4
|
+
runner, and the way a Rails app installs it. A thin Ruby wrapper around the
|
|
5
|
+
`mcptask_runner` Go binary: the gem ships the platform-specific binary inside
|
|
6
|
+
itself (`libexec/`) and exposes the same rake tasks as the legacy
|
|
7
|
+
`mcptask_runner` gem, delegating 1:1 to the CLI.
|
|
8
|
+
|
|
9
|
+
The legacy gem is retired. It gets critical fixes only, and it stays in
|
|
10
|
+
existence as the reference implementation the Go runner's conformance suite
|
|
11
|
+
measures against; everything new is written in Go. What you gain by moving:
|
|
12
|
+
Linux and Windows hosts, a scheduled job with no bundler in its path, and a
|
|
13
|
+
handful of places where the old implementation was simply wrong (its token
|
|
14
|
+
resolution read a different half of `.mcp.json` in each of three places, and
|
|
15
|
+
its stream reader died mid-run on an API error envelope).
|
|
7
16
|
|
|
8
17
|
One gem version carries exactly one binary version — `mcptask-rails-runner
|
|
9
18
|
X.Y.Z` contains the binary from the `vX.Y.Z` release of
|
|
10
19
|
[jchsoft/mcptask-releases](https://github.com/jchsoft/mcptask-releases).
|
|
11
20
|
|
|
21
|
+
`mcptask_runner:install` and `:update` copy that binary out of the gem to
|
|
22
|
+
`~/.mcptask/bin/mcptask_runner` and run it from there. The scheduled job runs
|
|
23
|
+
that copy, because a job pointing inside a gem directory dies at the next
|
|
24
|
+
`bundle update` — at 08:00, in a log nobody reads. So the pin is per project:
|
|
25
|
+
the Gemfile decides what a foreground `rake mcptask_runner:*` runs, while the
|
|
26
|
+
machine has one installed binary however many projects it carries, and the
|
|
27
|
+
newest version any of them offers is the one that stays. Install and update
|
|
28
|
+
print which version they wrote, and never replace a newer installed binary
|
|
29
|
+
with an older one.
|
|
30
|
+
|
|
12
31
|
## Requirements
|
|
13
32
|
|
|
14
33
|
- Ruby >= 3.0, Rails >= 6.0
|
|
@@ -36,8 +55,21 @@ The rake surface is identical — the migration is one Gemfile line:
|
|
|
36
55
|
+ gem "mcptask-rails-runner"
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
Then
|
|
40
|
-
|
|
58
|
+
Then:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
bundle install
|
|
62
|
+
FORCE=1 bundle exec rake mcptask_runner:install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Re-running install is what regenerates the scheduled job. The legacy launcher
|
|
66
|
+
runs `bundle exec rake` and logs the active version by grepping `Gemfile.lock`
|
|
67
|
+
for the old gem name — neither survives the rename, so a job left as it is
|
|
68
|
+
keeps working but stops recording what it ran. The regenerated one execs
|
|
69
|
+
`~/.mcptask/bin/mcptask_runner` directly, with no bundler in the 08:00 path.
|
|
70
|
+
|
|
71
|
+
Every `rake mcptask_runner:*` invocation and shell alias keeps working
|
|
72
|
+
unchanged. Your existing
|
|
41
73
|
`config/mcptask_runner.yml`, `.mcp.json` and `.claude/` setup are read by the
|
|
42
74
|
Go binary as-is. The only legacy task without a counterpart is
|
|
43
75
|
`mcptask_runner:prepare:permissions` — permission sync is part of
|
|
@@ -47,8 +79,8 @@ Go binary as-is. The only legacy task without a counterpart is
|
|
|
47
79
|
|
|
48
80
|
| Task | Delegates to |
|
|
49
81
|
|---|---|
|
|
50
|
-
| `mcptask_runner:install` | `mcptask_runner init` |
|
|
51
|
-
| `mcptask_runner:update` | `mcptask_runner update` |
|
|
82
|
+
| `mcptask_runner:install` | `mcptask_runner init` (from `~/.mcptask/bin`) |
|
|
83
|
+
| `mcptask_runner:update` | `mcptask_runner update` (from `~/.mcptask/bin`) |
|
|
52
84
|
| `mcptask_runner:bug_report` | `mcptask_runner bug-report` |
|
|
53
85
|
| `mcptask_runner:version` | `mcptask_runner version` |
|
|
54
86
|
| `mcptask_runner:manual:once` (`once_dry`, `today`, `daily`, `review`, `reviews`, `workflow`, `queue`) | `mcptask_runner run <mode>` |
|
|
@@ -61,7 +93,7 @@ the binary itself, exactly as before.
|
|
|
61
93
|
|
|
62
94
|
## Supported platforms
|
|
63
95
|
|
|
64
|
-
Platform gems exist for `arm64-darwin`, `x86_64-darwin`, `
|
|
96
|
+
Platform gems exist for `arm64-darwin`, `x86_64-darwin`, `aarch64-linux`,
|
|
65
97
|
`x86_64-linux` and `x64-mingw-ucrt`. On anything else bundler falls back to
|
|
66
98
|
the binary-less `ruby` platform gem, which fails at run time with
|
|
67
99
|
instructions: install the binary from
|
|
@@ -69,9 +101,11 @@ instructions: install the binary from
|
|
|
69
101
|
`install.sh` / npm `@mcptask/cli`) and point `MCPTASK_RUNNER_BIN` at it. The
|
|
70
102
|
same variable also serves offline or air-gapped installs.
|
|
71
103
|
|
|
72
|
-
The wrapper always executes the binary through an absolute path inside the
|
|
73
|
-
gem
|
|
74
|
-
|
|
104
|
+
The wrapper always executes the binary through an absolute path — inside the
|
|
105
|
+
gem for the run tasks, `~/.mcptask/bin` for install and update — and never
|
|
106
|
+
through PATH: the binary shares its name with the legacy tooling, and a PATH
|
|
107
|
+
lookup could pick up the wrong install. `MCPTASK_RUNNER_BIN` overrides both,
|
|
108
|
+
and install leaves `~/.mcptask/bin` untouched while it is set.
|
|
75
109
|
|
|
76
110
|
## Development
|
|
77
111
|
|
|
@@ -1,31 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
3
5
|
module McptaskRailsRunner
|
|
4
|
-
# Locates and executes the bundled Go binary.
|
|
6
|
+
# Locates, installs and executes the bundled Go binary.
|
|
5
7
|
module Binary
|
|
6
8
|
GEM_ROOT = File.expand_path("../..", __dir__)
|
|
7
9
|
|
|
8
10
|
SUPPORTED_PLATFORMS = %w[
|
|
9
11
|
arm64-darwin
|
|
10
12
|
x86_64-darwin
|
|
11
|
-
|
|
13
|
+
aarch64-linux
|
|
12
14
|
x86_64-linux
|
|
13
15
|
x64-mingw-ucrt
|
|
14
16
|
].freeze
|
|
15
17
|
|
|
18
|
+
# Where the binary is installed to live, outside every gem directory.
|
|
19
|
+
#
|
|
20
|
+
# The scheduled job runs this path, and it has to be one that survives a
|
|
21
|
+
# `bundle update`: a job pointing into
|
|
22
|
+
# gems/mcptask-rails-runner-<version>-<platform>/libexec dies the moment the
|
|
23
|
+
# gem is bumped, at 08:00, in a log nobody reads. Under the runner's own
|
|
24
|
+
# ~/.mcptask rather than /usr/local/bin, because `bundle install` has no
|
|
25
|
+
# sudo and a gem cannot write there. The Go side knows the same path — see
|
|
26
|
+
# InstalledBinary in internal/install/schedule.go.
|
|
27
|
+
INSTALL_DIR = File.join(Dir.home, ".mcptask", "bin")
|
|
28
|
+
|
|
29
|
+
# The line `mcptask_runner version` prints the version on.
|
|
30
|
+
VERSION_LINE = /Version:\s*(\S+)/
|
|
31
|
+
|
|
16
32
|
module_function
|
|
17
33
|
|
|
18
34
|
def executable_name
|
|
19
35
|
Gem.win_platform? ? "mcptask_runner.exe" : "mcptask_runner"
|
|
20
36
|
end
|
|
21
37
|
|
|
22
|
-
# Absolute path to the
|
|
23
|
-
# binary shares the mcptask_runner name with the legacy gem's rake
|
|
38
|
+
# Absolute path to the binary this gem runs. Never resolved through PATH:
|
|
39
|
+
# the binary shares the mcptask_runner name with the legacy gem's rake
|
|
24
40
|
# namespace, and a PATH lookup could pick up a stale or wrong install.
|
|
25
41
|
def path(gem_root: GEM_ROOT)
|
|
26
42
|
override = ENV["MCPTASK_RUNNER_BIN"]
|
|
27
43
|
return override unless override.nil? || override.empty?
|
|
28
44
|
|
|
45
|
+
bundled_path(gem_root: gem_root)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The copy that ships inside this gem, which is the one the Gemfile pins.
|
|
49
|
+
def bundled_path(gem_root: GEM_ROOT)
|
|
29
50
|
exe = File.join(gem_root, "libexec", executable_name)
|
|
30
51
|
return exe if File.file?(exe)
|
|
31
52
|
|
|
@@ -43,10 +64,91 @@ module McptaskRailsRunner
|
|
|
43
64
|
MSG
|
|
44
65
|
end
|
|
45
66
|
|
|
67
|
+
# The version-free path the installed copy lives at.
|
|
68
|
+
def installed_path(install_dir: INSTALL_DIR)
|
|
69
|
+
File.join(install_dir, executable_name)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Copies the bundled binary out of the gem, and returns what to run.
|
|
73
|
+
#
|
|
74
|
+
# The gem bootstraps the binary; it does not host it. Everything the
|
|
75
|
+
# scheduled job touches has to outlive `bundle update`, and a gem directory
|
|
76
|
+
# does not — so install and update put a copy where nothing but they will
|
|
77
|
+
# ever move it. A copy rather than a symlink: a link into the gem's libexec
|
|
78
|
+
# dangles on the same bundle update, which is the identical 08:00 failure
|
|
79
|
+
# moved one step along, and Windows wants a privilege for symlinks besides.
|
|
80
|
+
#
|
|
81
|
+
# Teaching the launcher to run `bundle exec rake` instead would fix the path
|
|
82
|
+
# and put bundler and Gemfile.lock back into the 08:00 path — the layer the
|
|
83
|
+
# Go port set out to remove.
|
|
84
|
+
#
|
|
85
|
+
# Newest wins, and says so. A host with several projects has several gem
|
|
86
|
+
# versions and one installed binary, so a project pinning an older gem must
|
|
87
|
+
# not quietly drag the machine's runner backwards.
|
|
88
|
+
def install!(install_dir: INSTALL_DIR, gem_root: GEM_ROOT, out: $stdout)
|
|
89
|
+
override = ENV["MCPTASK_RUNNER_BIN"]
|
|
90
|
+
unless override.nil? || override.empty?
|
|
91
|
+
out.puts "[mcptask_runner] MCPTASK_RUNNER_BIN is set — running #{override} " \
|
|
92
|
+
"and leaving #{installed_path(install_dir: install_dir)} alone."
|
|
93
|
+
return override
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
source = bundled_path(gem_root: gem_root)
|
|
97
|
+
target = installed_path(install_dir: install_dir)
|
|
98
|
+
|
|
99
|
+
existing = installed_version(target)
|
|
100
|
+
if existing && existing > Gem::Version.new(VERSION)
|
|
101
|
+
out.puts "[mcptask_runner] #{target} already holds #{existing}, which is newer than this " \
|
|
102
|
+
"gem's #{VERSION} — keeping it. Delete that file to install this gem's binary instead."
|
|
103
|
+
return target
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
copy(source, target)
|
|
107
|
+
out.puts "[mcptask_runner] installed mcptask_runner #{VERSION} into #{target}" \
|
|
108
|
+
"#{existing ? " (replacing #{existing})" : ''}."
|
|
109
|
+
target
|
|
110
|
+
end
|
|
111
|
+
|
|
46
112
|
# Replaces the current process, so the binary's exit code and signal
|
|
47
113
|
# handling reach the caller (rake) unchanged.
|
|
48
114
|
def exec!(*args)
|
|
49
115
|
Kernel.exec(path, *args)
|
|
50
116
|
end
|
|
117
|
+
|
|
118
|
+
# Installs the binary outside the gem first, then runs that copy.
|
|
119
|
+
#
|
|
120
|
+
# Used by install and update, the two tasks that generate the scheduled job:
|
|
121
|
+
# the job runs whichever binary is executing them, so it has to be the copy
|
|
122
|
+
# at the version-free path rather than the one inside this gem.
|
|
123
|
+
def exec_installed!(*args)
|
|
124
|
+
Kernel.exec(install!, *args)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# What the installed copy reports about itself, or nil when there is none —
|
|
128
|
+
# or when it cannot be asked, which a stray file at that path cannot be.
|
|
129
|
+
#
|
|
130
|
+
# Asked of the binary rather than recorded in a marker file beside it: the
|
|
131
|
+
# binary is what actually runs at 08:00, and a marker can only be right
|
|
132
|
+
# about installs that went through here.
|
|
133
|
+
def installed_version(target)
|
|
134
|
+
return nil unless File.file?(target)
|
|
135
|
+
|
|
136
|
+
banner = IO.popen([target, "version"], err: File::NULL, &:read)
|
|
137
|
+
raw = banner.to_s[VERSION_LINE, 1]
|
|
138
|
+
raw && Gem::Version.new(raw)
|
|
139
|
+
rescue StandardError
|
|
140
|
+
nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Written beside the target and renamed into place: a half-copied binary
|
|
144
|
+
# over a working one is a scheduled job that fails on a corrupt file, and
|
|
145
|
+
# the rename also sidesteps the ETXTBSY a running copy would give.
|
|
146
|
+
def copy(source, target)
|
|
147
|
+
FileUtils.mkdir_p(File.dirname(target))
|
|
148
|
+
staged = "#{target}.new"
|
|
149
|
+
FileUtils.cp(source, staged)
|
|
150
|
+
FileUtils.chmod(0o755, staged)
|
|
151
|
+
File.rename(staged, target)
|
|
152
|
+
end
|
|
51
153
|
end
|
|
52
154
|
end
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module McptaskRailsRunner
|
|
4
|
-
# The gem version
|
|
5
|
-
#
|
|
6
|
-
|
|
4
|
+
# The gem version names the mcptask_runner binary release it carries (tag
|
|
5
|
+
# v#{VERSION} in jchsoft/mcptask-releases). Bump both together.
|
|
6
|
+
#
|
|
7
|
+
# What it does not name is the binary the host runs at 08:00. That one is
|
|
8
|
+
# installed outside every gem directory, by install and update, and a machine
|
|
9
|
+
# has one of it however many projects and gem versions it carries — see
|
|
10
|
+
# Binary::INSTALL_DIR. So the pin is per project and per foreground rake
|
|
11
|
+
# invocation; the installed copy is per machine, and the newest binary any of
|
|
12
|
+
# those projects offers is the one that stays.
|
|
13
|
+
VERSION = "0.3.0"
|
|
7
14
|
end
|
|
@@ -4,18 +4,25 @@
|
|
|
4
4
|
# a customer is only a Gemfile line swap. Every task delegates 1:1 to the
|
|
5
5
|
# bundled Go CLI via Kernel#exec — the binary's exit code and signal handling
|
|
6
6
|
# reach the caller unchanged, and nothing runs after the delegation line.
|
|
7
|
+
#
|
|
8
|
+
# Two of them delegate to the installed copy instead of the bundled one. install
|
|
9
|
+
# and update are the tasks that generate the scheduled job, and the job runs
|
|
10
|
+
# whichever binary generated it: pointing it inside this gem would have it die
|
|
11
|
+
# at the next `bundle update`, which deletes that directory. Everything else
|
|
12
|
+
# runs the binary the Gemfile pins, which is what a foreground rake invocation
|
|
13
|
+
# should do.
|
|
7
14
|
|
|
8
15
|
require "mcptask_rails_runner"
|
|
9
16
|
|
|
10
17
|
namespace :mcptask_runner do
|
|
11
18
|
desc "Install skills, permissions, token and scheduled job into this project"
|
|
12
19
|
task :install do
|
|
13
|
-
McptaskRailsRunner::Binary.
|
|
20
|
+
McptaskRailsRunner::Binary.exec_installed!("init")
|
|
14
21
|
end
|
|
15
22
|
|
|
16
23
|
desc "Refresh bundled skills and helpers"
|
|
17
24
|
task :update do
|
|
18
|
-
McptaskRailsRunner::Binary.
|
|
25
|
+
McptaskRailsRunner::Binary.exec_installed!("update")
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
desc "File a bug against the project with the last run log attached"
|