@ivan-murzak/runner-manager 0.0.0-bootstrap.1 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivan Murzak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,9 +1,102 @@
1
- # @ivan-murzak/runner-manager
1
+ # @ivan-murzak/runner-manager (npm wrapper)
2
2
 
3
- Placeholder. This version exists only so that npm trusted publishing can be configured for
4
- this package name; npm cannot attach a trusted publisher to a package that does not exist.
3
+ Local-first autoscaling manager for ephemeral GitHub Actions self-hosted
4
+ runners, with a CLI and a Ratatui TUI.
5
5
 
6
- It carries no binary and is published under the `bootstrap` dist-tag, so it is not what
7
- `npm install` resolves. The first real release replaces it.
6
+ > **Read this before you install.** Using this tool means installing the
7
+ > project's published GitHub App, which declares **Repository → Administration:
8
+ > Read and write** — a permission that also allows deleting, renaming and
9
+ > transferring the repository, and adding or removing collaborators. It applies
10
+ > even if you only ever use `runner-manager` as a read-only dashboard. The full
11
+ > permission set and the narrower organization-scoped alternative are in
12
+ > [What you are granting](#what-you-are-granting) below.
8
13
 
9
- Project: <https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI>
14
+ ```sh
15
+ npm i -g @ivan-murzak/runner-manager
16
+ runner-manager --version
17
+ ```
18
+
19
+ This package is a thin wrapper. The binary itself lives in one of five
20
+ per-platform packages, declared here as `optionalDependencies`, so npm installs
21
+ only the one that matches your OS and CPU:
22
+
23
+ | Platform package | os / cpu | Rust target |
24
+ |---|---|---|
25
+ | `@ivan-murzak/runner-manager-win32-x64` | `win32` / `x64` | `x86_64-pc-windows-msvc` |
26
+ | `@ivan-murzak/runner-manager-darwin-arm64` | `darwin` / `arm64` | `aarch64-apple-darwin` |
27
+ | `@ivan-murzak/runner-manager-darwin-x64` | `darwin` / `x64` | `x86_64-apple-darwin` |
28
+ | `@ivan-murzak/runner-manager-linux-x64` | `linux` / `x64` | `x86_64-unknown-linux-gnu` |
29
+ | `@ivan-murzak/runner-manager-linux-arm64` | `linux` / `arm64` | `aarch64-unknown-linux-gnu` |
30
+
31
+ Each platform package records, in its `package.json`, the release archive its
32
+ binary was taken from and that archive's published SHA-256 — the same digest
33
+ listed in the release's `SHA256SUMS`.
34
+
35
+ ## Read this before `service install`
36
+
37
+ **An `npm i -g` binary does not have a fixed home, and this product records an
38
+ absolute path.**
39
+
40
+ `npm i -g` installs into the *active* Node installation's global prefix. If you
41
+ manage Node with `nvm`, `fnm`, `volta`, `asdf`, Homebrew, or a Windows
42
+ installer upgrade, that prefix is different for every Node version — something
43
+ like:
44
+
45
+ ```text
46
+ ~/.nvm/versions/node/v20.11.0/bin/runner-manager
47
+ ~/.nvm/versions/node/v22.3.0/bin/runner-manager <- after `nvm install 22`
48
+ ```
49
+
50
+ `runner-manager service install` resolves and records the **absolute** path of
51
+ the binary at the moment you run it. So switching Node versions after
52
+ installing the service leaves the service pointing at a path that no longer
53
+ exists, and nothing tells you until the next unattended boot, when the agent
54
+ does not come up.
55
+
56
+ `runner-manager service status` detects this and reports the recorded path as
57
+ **stale** rather than reporting the service as healthy. If you see that:
58
+
59
+ ```sh
60
+ npm i -g @ivan-murzak/runner-manager # into the Node version you are now using
61
+ runner-manager service install # re-records the new absolute path
62
+ ```
63
+
64
+ **If you want a boot-start service, prefer the install script.** It installs to
65
+ `~/.local/bin` (macOS, Linux) or `%LOCALAPPDATA%\Programs\runner-manager`
66
+ (Windows), neither of which moves when a toolchain moves:
67
+
68
+ ```sh
69
+ curl -fsSL https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI/releases/latest/download/install.sh | sh
70
+ ```
71
+
72
+ ```powershell
73
+ irm https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI/releases/latest/download/install.ps1 | iex
74
+ ```
75
+
76
+ This package is the right choice when you already manage tooling with npm and
77
+ you run `runner-manager` interactively or from a script, rather than as a
78
+ boot-start service.
79
+
80
+ ## Windows on ARM
81
+
82
+ npm will not install an `"cpu": ["x64"]` package onto an arm64 host, and no
83
+ arm64 Windows build is published, so this package cannot serve Windows on ARM.
84
+ The install script can: it uses the x64 build through the built-in emulation
85
+ layer.
86
+
87
+ ## What you are granting
88
+
89
+ Using this tool means installing the project's published GitHub App, and that
90
+ App declares **Repository → Administration: Read and write**. That permission
91
+ also allows deleting, renaming and transferring the repository, and adding or
92
+ removing collaborators. It applies even if you only ever use `runner-manager`
93
+ as a read-only dashboard.
94
+
95
+ The full permission table, why the grant is unavoidable at repository scope,
96
+ and why organization scope is materially narrower are in the
97
+ [repository README](https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI#what-you-are-granting).
98
+ Read it before you run `auth login`.
99
+
100
+ ## Licence
101
+
102
+ MIT. Source: <https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI>
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ //
3
+ // The npm wrapper's entry point (task a3, D11).
4
+ //
5
+ // ---------------------------------------------------------------------------
6
+ // WHY A SHIM AND NOT A postinstall DOWNLOAD.
7
+ // ---------------------------------------------------------------------------
8
+ // The other common shape for shipping a binary through npm is a `postinstall`
9
+ // script that downloads it. That shape needs network access at install time,
10
+ // breaks behind a proxy and inside an offline CI cache, and -- the part that
11
+ // matters here -- it fetches an artifact that npm's own integrity hashes never
12
+ // covered. The esbuild shape used instead puts each binary inside a real npm
13
+ // package, so npm's registry integrity hash covers the exact bytes that end up
14
+ // on disk, and `--ignore-scripts` (increasingly a default in locked-down CI)
15
+ // does not silently produce an installation with no binary in it.
16
+ //
17
+ // ---------------------------------------------------------------------------
18
+ // WHY THE PLATFORM PACKAGES ARE `optionalDependencies`.
19
+ // ---------------------------------------------------------------------------
20
+ // A package whose `os`/`cpu` do not match the host is SKIPPED when it is
21
+ // optional and is a hard install failure when it is not. Five platform
22
+ // packages on a normal `dependencies` line would therefore fail the install
23
+ // for every user on four of the five platforms. The cost is that a genuinely
24
+ // missing package is indistinguishable from a skipped one at install time,
25
+ // which is what the diagnostics below exist to untangle at run time.
26
+ //
27
+ // ---------------------------------------------------------------------------
28
+ // THIS FILE IS COMMITTED; THE package.json FILES BESIDE IT ARE GENERATED.
29
+ // ---------------------------------------------------------------------------
30
+ // `.github/scripts/channels.sh npm-manifests` writes the manifests at release
31
+ // time so every version and every published digest comes from the release that
32
+ // is happening. This file has no version in it and never needs one.
33
+
34
+ "use strict";
35
+
36
+ const path = require("node:path");
37
+ const { spawnSync } = require("node:child_process");
38
+
39
+ // Must agree with `PUBLISHED_TARGETS` in `.github/scripts/channels.sh`.
40
+ // `crates/app/tests/release_channels.rs` compares the two, because a package
41
+ // renamed on one side and not the other produces a wrapper that installs
42
+ // cleanly and then cannot find its own binary.
43
+ const PLATFORMS = {
44
+ "darwin arm64": ["@ivan-murzak/runner-manager-darwin-arm64", "runner-manager"],
45
+ "darwin x64": ["@ivan-murzak/runner-manager-darwin-x64", "runner-manager"],
46
+ "linux arm64": ["@ivan-murzak/runner-manager-linux-arm64", "runner-manager"],
47
+ "linux x64": ["@ivan-murzak/runner-manager-linux-x64", "runner-manager"],
48
+ "win32 x64": ["@ivan-murzak/runner-manager-win32-x64", "runner-manager.exe"],
49
+ };
50
+
51
+ function fail(lines) {
52
+ for (const line of lines) {
53
+ process.stderr.write(line + "\n");
54
+ }
55
+ process.exit(1);
56
+ }
57
+
58
+ const key = process.platform + " " + process.arch;
59
+ const entry = PLATFORMS[key];
60
+
61
+ if (!entry) {
62
+ const extra = [];
63
+ if (process.platform === "win32") {
64
+ // Windows on ARM runs the x64 build through the built-in emulation layer,
65
+ // but npm will not install an `"cpu": ["x64"]` package onto an arm64 host,
66
+ // so this is the one platform npm cannot serve and the install script can.
67
+ extra.push(
68
+ "",
69
+ "Windows on ARM is supported by the install script, which uses the x64",
70
+ "build through the built-in emulation layer:",
71
+ "",
72
+ " irm https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI/releases/latest/download/install.ps1 | iex",
73
+ );
74
+ }
75
+ fail([
76
+ `runner-manager: no published binary for ${process.platform} ${process.arch}.`,
77
+ "",
78
+ "Published platforms: " + Object.keys(PLATFORMS).join(", ") + ".",
79
+ "Build from source instead: cargo install runner-manager",
80
+ ...extra,
81
+ ]);
82
+ }
83
+
84
+ const [packageName, binaryName] = entry;
85
+
86
+ let binary;
87
+ try {
88
+ // Resolved through `package.json` rather than through the package's main
89
+ // entry point: these packages have no JavaScript in them at all, so there is
90
+ // no main to resolve.
91
+ binary = path.join(
92
+ path.dirname(require.resolve(packageName + "/package.json")),
93
+ "bin",
94
+ binaryName,
95
+ );
96
+ } catch (error) {
97
+ fail([
98
+ `runner-manager: the platform package ${packageName} is not installed.`,
99
+ "",
100
+ "It is an optionalDependency, so npm skips it silently in three cases:",
101
+ "",
102
+ " * the install ran with --no-optional or --omit=optional",
103
+ " * the install ran on a different platform (a lockfile or a Docker",
104
+ " image built on one OS and used on another)",
105
+ " * the registry was unreachable when the optional dependency was fetched",
106
+ "",
107
+ "Reinstall with optional dependencies enabled:",
108
+ "",
109
+ " npm install -g @ivan-murzak/runner-manager",
110
+ "",
111
+ `(resolution error: ${error && error.message ? error.message : error})`,
112
+ ]);
113
+ }
114
+
115
+ // `stdio: "inherit"` because this is a TUI as well as a CLI: the child needs
116
+ // the real terminal, not a pipe, or Ratatui has no size to draw into and no
117
+ // key events to read.
118
+ //
119
+ // The child's exit code is propagated. A wrapper that always exits 0 makes
120
+ // every `runner-manager` invocation look successful to a shell script, to a
121
+ // CI step, and to the service manager that restarts it on failure.
122
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
123
+
124
+ if (result.error) {
125
+ fail([
126
+ `runner-manager: could not execute ${binary}`,
127
+ "",
128
+ String(result.error.message || result.error),
129
+ ]);
130
+ }
131
+
132
+ // Killed by a signal: report it as a shell does, rather than as exit 0.
133
+ if (result.signal) {
134
+ process.stderr.write(`runner-manager: terminated by signal ${result.signal}\n`);
135
+ process.exit(1);
136
+ }
137
+
138
+ process.exit(result.status === null ? 1 : result.status);
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "@ivan-murzak/runner-manager",
3
- "version": "0.0.0-bootstrap.1",
4
- "description": "Placeholder so trusted publishing can be configured. Not a release; see the repository.",
3
+ "version": "0.1.2",
4
+ "description": "Local-first autoscaling manager for ephemeral GitHub Actions self-hosted runners, with a CLI and a Ratatui TUI.",
5
5
  "license": "MIT",
6
6
  "author": "Ivan Murzak (https://github.com/IvanMurzak)",
7
7
  "homepage": "https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI",
8
8
  "repository": { "type": "git", "url": "git+https://github.com/IvanMurzak/GitHub-Runner-Scaler-UI.git" },
9
- "files": [ "README.md" ]
9
+ "engines": { "node": ">=18" },
10
+ "bin": { "runner-manager": "bin/runner-manager.cjs" },
11
+ "files": [ "bin/", "README.md" ],
12
+ "optionalDependencies": {
13
+ "@ivan-murzak/runner-manager-win32-x64": "0.1.2",
14
+ "@ivan-murzak/runner-manager-darwin-arm64": "0.1.2",
15
+ "@ivan-murzak/runner-manager-darwin-x64": "0.1.2",
16
+ "@ivan-murzak/runner-manager-linux-x64": "0.1.2",
17
+ "@ivan-murzak/runner-manager-linux-arm64": "0.1.2"
18
+ }
10
19
  }