@mcptask/cli 0.2.3

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.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @mcptask/cli
2
+
3
+ The [mcptask](https://mcptask.online) runner — drives Claude Code through the
4
+ tasks on mcptask.online.
5
+
6
+ ```bash
7
+ npx @mcptask/cli init
8
+ ```
9
+
10
+ Run that in the project you want the runner to work on. It installs the skills,
11
+ helper scripts and configuration the runner needs, and prints how to switch on a
12
+ scheduled job — it does not switch one on for you.
13
+
14
+ ```bash
15
+ npx @mcptask/cli run today # work today's tasks
16
+ npx @mcptask/cli version # version and resolved configuration
17
+ ```
18
+
19
+ ## What this package is
20
+
21
+ A thin wrapper around a single static binary. It carries no binary of its own:
22
+ publishing six of them in one tarball would make every project download five it
23
+ cannot run, so the postinstall downloads the one archive this platform needs and
24
+ verifies it against the release's published `checksums.txt` before unpacking it.
25
+
26
+ macOS, Linux and Windows, on amd64 and arm64. The binary needs no Go and no Ruby.
27
+
28
+ The wrapper forwards arguments and exit codes untouched — the runner's exit codes
29
+ are a contract that scheduled jobs read, and a wrapper that flattened them to 0/1
30
+ would break every one of them.
31
+
32
+ ## Not a JS project?
33
+
34
+ The same binary installs without npm:
35
+
36
+ ```bash
37
+ curl -fsSL https://github.com/jchsoft/mcptask-releases/releases/latest/download/install.sh | sh
38
+ ```
39
+
40
+ Or `brew install jchsoft/tap/mcptask_runner` on macOS, and on Windows:
41
+
42
+ ```powershell
43
+ scoop bucket add jchsoft https://github.com/jchsoft/scoop-bucket
44
+ scoop install mcptask_runner
45
+ ```
46
+
47
+ Once installed any way, it updates itself:
48
+
49
+ ```bash
50
+ mcptask_runner update --self
51
+ ```
52
+
53
+ ## Environment
54
+
55
+ | Variable | Effect |
56
+ | --- | --- |
57
+ | `MCPTASK_VERSION` | install a specific release instead of the one matching this package |
58
+ | `MCPTASK_NO_UPDATE_CHECK` | set to `1` to stop the runner mentioning newer releases |
59
+
60
+ The package version is the release it installs, so `@mcptask/cli@0.2.3` gives you
61
+ `mcptask_runner` 0.2.3.
62
+
63
+ ## Where things live
64
+
65
+ Binaries and checksums: [jchsoft/mcptask-releases](https://github.com/jchsoft/mcptask-releases).
66
+
67
+ ---
68
+
69
+ Proprietary — © JCHSoft. All rights reserved.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ // Thin wrapper: run the binary the postinstall script downloaded (#11273).
3
+ //
4
+ // It exists so `npx @mcptask/cli init` works, and it deliberately does nothing
5
+ // beyond handing over — arguments through untouched, stdio inherited so the
6
+ // runner's own progress output and prompts behave as if it had been invoked
7
+ // directly, and its exit code passed back. The runner's exit codes are a
8
+ // documented contract, and a wrapper that collapsed them to 0/1 would break
9
+ // every scheduled job that reads them.
10
+ 'use strict'
11
+
12
+ const path = require('node:path')
13
+ const fs = require('node:fs')
14
+ const { spawnSync } = require('node:child_process')
15
+
16
+ const binary = process.platform === 'win32' ? 'mcptask_runner.exe' : 'mcptask_runner'
17
+ const executable = path.join(__dirname, '..', 'vendor', binary)
18
+
19
+ if (!fs.existsSync(executable)) {
20
+ console.error(
21
+ '@mcptask/cli: the runner binary is missing — the postinstall download did ' +
22
+ 'not complete.\nReinstall with `npm install @mcptask/cli`, or run ' +
23
+ '`node install.js` inside the package.'
24
+ )
25
+ process.exit(1)
26
+ }
27
+
28
+ const result = spawnSync(executable, process.argv.slice(2), { stdio: 'inherit' })
29
+
30
+ if (result.error) {
31
+ console.error(`@mcptask/cli: could not start the runner — ${result.error.message}`)
32
+ process.exit(1)
33
+ }
34
+
35
+ // A process killed by a signal has a null status; report it the way a shell
36
+ // does rather than as a success.
37
+ process.exit(result.status === null ? 1 : result.status)
package/install.js ADDED
@@ -0,0 +1,143 @@
1
+ // postinstall: fetch the mcptask_runner binary for this platform (#11273).
2
+ //
3
+ // The npm package carries no binary of its own — publishing six of them in one
4
+ // tarball would make every JS project download five it cannot run. Instead the
5
+ // package version tracks the release tag, and this script downloads the one
6
+ // archive this platform needs and verifies it against the release's checksums.
7
+ //
8
+ // Node's standard library only, on purpose: a postinstall script with
9
+ // dependencies has to be installed before it can install, and every dependency
10
+ // here would run in the same privileged position on every host.
11
+ 'use strict'
12
+
13
+ const crypto = require('node:crypto')
14
+ const fs = require('node:fs')
15
+ const https = require('node:https')
16
+ const os = require('node:os')
17
+ const path = require('node:path')
18
+ const { execFileSync } = require('node:child_process')
19
+
20
+ const REPO = process.env.MCPTASK_RELEASE_REPO || 'jchsoft/mcptask-releases'
21
+ const BINARY = process.platform === 'win32' ? 'mcptask_runner.exe' : 'mcptask_runner'
22
+ const VENDOR = path.join(__dirname, 'vendor')
23
+
24
+ // npm normalises the package version; the release tag it came from is that
25
+ // version with a leading v. MCPTASK_VERSION overrides both, which is what makes
26
+ // a pre-release installable without republishing the package.
27
+ const version = process.env.MCPTASK_VERSION || `v${require('./package.json').version}`
28
+
29
+ function platformSlug () {
30
+ const goos = { darwin: 'darwin', linux: 'linux', win32: 'windows' }[process.platform]
31
+ const goarch = { x64: 'amd64', arm64: 'arm64' }[process.arch]
32
+ if (!goos || !goarch) {
33
+ throw new Error(
34
+ `unsupported platform ${process.platform}/${process.arch} — ` +
35
+ 'the release builds darwin, linux and windows on amd64 and arm64'
36
+ )
37
+ }
38
+ return { goos, goarch }
39
+ }
40
+
41
+ // GitHub redirects release downloads to a storage host, so redirects have to be
42
+ // followed by hand: https.get does not do it.
43
+ function get (url, redirectsLeft = 5) {
44
+ return new Promise((resolve, reject) => {
45
+ const headers = { 'user-agent': '@mcptask/cli' }
46
+ // Only needed while the release repository is private.
47
+ const token = process.env.MCPTASK_GITHUB_TOKEN || process.env.GITHUB_TOKEN
48
+ if (token) headers.authorization = `Bearer ${token}`
49
+
50
+ https.get(url, { headers }, (res) => {
51
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
52
+ res.resume()
53
+ if (redirectsLeft === 0) return reject(new Error(`too many redirects for ${url}`))
54
+ return resolve(get(res.headers.location, redirectsLeft - 1))
55
+ }
56
+ if (res.statusCode !== 200) {
57
+ res.resume()
58
+ return reject(new Error(`GET ${url} failed with HTTP ${res.statusCode}`))
59
+ }
60
+ const chunks = []
61
+ res.on('data', (c) => chunks.push(c))
62
+ res.on('end', () => resolve(Buffer.concat(chunks)))
63
+ res.on('error', reject)
64
+ }).on('error', reject)
65
+ })
66
+ }
67
+
68
+ // The archive is only unpacked once its hash matches what the release published.
69
+ // This runs as a postinstall script, so the binary it produces will be executed
70
+ // by the developer without further inspection — an unverified download here is
71
+ // an unverified download straight onto PATH.
72
+ function verify (archiveName, archive, checksums) {
73
+ const digest = crypto.createHash('sha256').update(archive).digest('hex')
74
+ const line = checksums
75
+ .toString('utf8')
76
+ .split('\n')
77
+ .find((l) => l.trim().endsWith(archiveName))
78
+ if (!line) throw new Error(`checksums.txt does not list ${archiveName}`)
79
+
80
+ const expected = line.trim().split(/\s+/)[0]
81
+ if (expected !== digest) {
82
+ throw new Error(
83
+ `checksum mismatch for ${archiveName}\n expected ${expected}\n got ${digest}`
84
+ )
85
+ }
86
+ }
87
+
88
+ function extract (archivePath, destination) {
89
+ if (archivePath.endsWith('.zip')) {
90
+ // Windows: PowerShell rather than a zip dependency.
91
+ execFileSync('powershell', [
92
+ '-NoProfile', '-NonInteractive', '-Command',
93
+ `Expand-Archive -Path "${archivePath}" -DestinationPath "${destination}" -Force`
94
+ ], { stdio: 'inherit' })
95
+ } else {
96
+ // bsdtar/GNU tar, present on every macOS and Linux host.
97
+ execFileSync('tar', ['-xzf', archivePath, '-C', destination], { stdio: 'inherit' })
98
+ }
99
+ }
100
+
101
+ async function main () {
102
+ const { goos, goarch } = platformSlug()
103
+ const bare = version.replace(/^v/, '')
104
+ const ext = goos === 'windows' ? 'zip' : 'tar.gz'
105
+ const archiveName = `mcptask_runner_${bare}_${goos}_${goarch}.${ext}`
106
+ const base = `https://github.com/${REPO}/releases/download/${version}`
107
+
108
+ console.log(`@mcptask/cli: downloading ${archiveName}`)
109
+ const [archive, checksums] = await Promise.all([
110
+ get(`${base}/${archiveName}`),
111
+ get(`${base}/checksums.txt`)
112
+ ])
113
+
114
+ verify(archiveName, archive, checksums)
115
+
116
+ fs.rmSync(VENDOR, { recursive: true, force: true })
117
+ fs.mkdirSync(VENDOR, { recursive: true })
118
+
119
+ const staging = fs.mkdtempSync(path.join(os.tmpdir(), 'mcptask-'))
120
+ try {
121
+ const archivePath = path.join(staging, archiveName)
122
+ fs.writeFileSync(archivePath, archive)
123
+ extract(archivePath, staging)
124
+
125
+ const from = path.join(staging, BINARY)
126
+ if (!fs.existsSync(from)) throw new Error(`the archive did not contain ${BINARY}`)
127
+ fs.copyFileSync(from, path.join(VENDOR, BINARY))
128
+ // npm does not preserve the executable bit through this path, and the
129
+ // wrapper execs the file directly rather than through a shell.
130
+ fs.chmodSync(path.join(VENDOR, BINARY), 0o755)
131
+ } finally {
132
+ fs.rmSync(staging, { recursive: true, force: true })
133
+ }
134
+
135
+ console.log(`@mcptask/cli: installed mcptask_runner ${version}`)
136
+ }
137
+
138
+ main().catch((err) => {
139
+ console.error(`@mcptask/cli: ${err.message}`)
140
+ // Fail the install rather than leaving a package whose only command is
141
+ // missing; `npx @mcptask/cli` would otherwise fail later and less clearly.
142
+ process.exit(1)
143
+ })
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@mcptask/cli",
3
+ "version": "0.2.3",
4
+ "description": "mcptask runner — drives Claude Code through the tasks on mcptask.online",
5
+ "homepage": "https://mcptask.online",
6
+ "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/jchsoft/mcptask-releases.git"
10
+ },
11
+ "bin": {
12
+ "mcptask_runner": "bin/mcptask_runner.js"
13
+ },
14
+ "scripts": {
15
+ "postinstall": "node install.js"
16
+ },
17
+ "files": [
18
+ "bin/mcptask_runner.js",
19
+ "install.js",
20
+ "README.md"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "os": [
26
+ "darwin",
27
+ "linux",
28
+ "win32"
29
+ ],
30
+ "cpu": [
31
+ "x64",
32
+ "arm64"
33
+ ]
34
+ }