@firedmosquito831/my-claude-code 7.79.0 → 7.79.1
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 +30 -6
- package/bin/postinstall.js +15 -2
- package/bin/runtime-install.js +114 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,11 +21,31 @@ release of the right shape:
|
|
|
21
21
|
`install.sh`), which brings `uv`, Python 3.14 and every `mcc-*` command;
|
|
22
22
|
- **the native desktop application, where there is a desktop session** —
|
|
23
23
|
Windows and macOS always, Linux only with `DISPLAY` or `WAYLAND_DISPLAY`.
|
|
24
|
-
Over SSH, in CI, on a headless server or in WSL without a display
|
|
25
|
-
|
|
24
|
+
Over SSH, in CI, on a headless server or in WSL — with or without a display,
|
|
25
|
+
because the desktop app for a Windows PC is the Windows one — it installs
|
|
26
|
+
the server alone, without even the installer's desktop flag, so no
|
|
26
27
|
shortcuts are created for a screen that does not exist.
|
|
27
28
|
|
|
28
|
-
It prints one line saying what it decided and why before it does anything
|
|
29
|
+
It prints one line saying what it decided and why before it does anything
|
|
30
|
+
(npm shows a hook's output only with `--foreground-scripts`).
|
|
31
|
+
|
|
32
|
+
### With a system Node, sudo, and a user-level prefix
|
|
33
|
+
|
|
34
|
+
My Claude Code installs **per user**. With a system Node whose global folder
|
|
35
|
+
belongs to root, `sudo npm install -g` gives you the `mcc` command and nothing
|
|
36
|
+
else: run as root on behalf of your user, the hook installs no server for root
|
|
37
|
+
(a root-owned copy you could not use, and a root server on your port) and the
|
|
38
|
+
server is installed by your first `mcc` — as you, not root. Simpler still, give
|
|
39
|
+
npm a folder you own and skip sudo entirely:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm config set prefix ~/.npm-global # then add ~/.npm-global/bin to PATH
|
|
43
|
+
npm install -g @firedmosquito831/my-claude-code
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To install for root on purpose, set `MCC_INSTALL_ALLOW_ROOT=1` (or run
|
|
47
|
+
`mcc install --allow-root`). A root shell, a root VPS or a Docker image — no
|
|
48
|
+
`SUDO_USER` — is not affected.
|
|
29
49
|
|
|
30
50
|
| Platform | What the desktop half installs |
|
|
31
51
|
| --- | --- |
|
|
@@ -64,8 +84,10 @@ too, each printing one line saying so.
|
|
|
64
84
|
|
|
65
85
|
If the server installs and the desktop half fails — a flaky network, a 404 —
|
|
66
86
|
the install stays green and tells you how to retry just that half. A failing
|
|
67
|
-
*server* install fails `npm install -g` with it
|
|
68
|
-
|
|
87
|
+
*server* install fails `npm install -g` with it — and that includes `curl`
|
|
88
|
+
being missing or the installer download failing on Linux and macOS, which
|
|
89
|
+
fail with a `WARNING:` line saying so rather than a "done" with nothing
|
|
90
|
+
installed.
|
|
69
91
|
|
|
70
92
|
## Run without installing globally
|
|
71
93
|
|
|
@@ -89,7 +111,9 @@ npm uninstall -g @firedmosquito831/my-claude-code # removes only this launcher
|
|
|
89
111
|
|
|
90
112
|
Run `mcc uninstall` **first**: npm removes the launcher it installed and nothing
|
|
91
113
|
else, so uninstalling the package on its own leaves the Python server in place.
|
|
92
|
-
|
|
114
|
+
On Windows `mcc uninstall` keeps the Start Menu entry when it is the
|
|
115
|
+
installed desktop app's own. The desktop application is uninstalled the way
|
|
116
|
+
its platform expects — Add or
|
|
93
117
|
remove programs on Windows, deleting it from `~/Applications` on macOS,
|
|
94
118
|
`sudo dpkg -r my-claude-code-desktop` or `install-desktop.sh --uninstall` on
|
|
95
119
|
Linux.
|
package/bin/postinstall.js
CHANGED
|
@@ -88,8 +88,21 @@ async function main() {
|
|
|
88
88
|
|
|
89
89
|
// No argv: `npm install -g` passes none. Overrides here are the environment
|
|
90
90
|
// variable, which is the only channel a package manager or Dockerfile has.
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
//
|
|
92
|
+
// 7.79.1: as root through sudo on behalf of a user, nothing is installed
|
|
93
|
+
// for root and the hook still exits 0 (`refusedStatus: 0`), so npm keeps the
|
|
94
|
+
// `mcc` command it wrote -- with a system Node that is the one thing sudo
|
|
95
|
+
// was needed for. The user's first `mcc` then installs the server, as them.
|
|
96
|
+
let refused = false;
|
|
97
|
+
const status = await runtime.performInstall({
|
|
98
|
+
argv: [],
|
|
99
|
+
log: note,
|
|
100
|
+
refusedStatus: 0,
|
|
101
|
+
onRefused: () => {
|
|
102
|
+
refused = true;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
if (status === 0 && !refused) {
|
|
93
106
|
note("done. Open a new terminal so PATH picks up the mcc-* commands.");
|
|
94
107
|
}
|
|
95
108
|
return status;
|
package/bin/runtime-install.js
CHANGED
|
@@ -62,6 +62,10 @@ const OVERRIDE_FLAGS = [
|
|
|
62
62
|
// environment without any help from here.
|
|
63
63
|
"--no-restart",
|
|
64
64
|
"--no-start",
|
|
65
|
+
// 7.79.1: install for root even when this runs as root through sudo on
|
|
66
|
+
// behalf of another user. Passed through to install.sh, which refuses that
|
|
67
|
+
// case too; the environment form is MCC_INSTALL_ALLOW_ROOT=1.
|
|
68
|
+
"--allow-root",
|
|
65
69
|
];
|
|
66
70
|
|
|
67
71
|
/**
|
|
@@ -89,7 +93,11 @@ By default it installs the server (always) and, when this machine has a
|
|
|
89
93
|
desktop session, the desktop application in its native form: the setup.exe on
|
|
90
94
|
Windows, the .dmg into ~/Applications on macOS, the .deb (or the tarball's
|
|
91
95
|
per-user installer) on Linux. On a headless box, over SSH, in CI or in WSL
|
|
92
|
-
|
|
96
|
+
(display or not -- the desktop app for a Windows PC is the Windows one) it
|
|
97
|
+
installs the server only and says so.
|
|
98
|
+
|
|
99
|
+
My Claude Code installs per user. Run as root through sudo on behalf of
|
|
100
|
+
another user, it installs nothing and says how to install for that user.
|
|
93
101
|
|
|
94
102
|
--server-only install the server, never the desktop app
|
|
95
103
|
--desktop-only install the desktop app, leave the server alone
|
|
@@ -98,6 +106,7 @@ without a display it installs the server only and says so.
|
|
|
98
106
|
--no-restart do not stop a My Claude Code server that is already
|
|
99
107
|
running. One is still started if nothing answers.
|
|
100
108
|
--no-start do not stop and do not start any server
|
|
109
|
+
--allow-root install for root even under sudo (MCC_INSTALL_ALLOW_ROOT=1)
|
|
101
110
|
--help this text
|
|
102
111
|
|
|
103
112
|
The official installer restarts the server on the configured port by default
|
|
@@ -120,7 +129,7 @@ the same release before it is run, and deleted if the digest does not match.`;
|
|
|
120
129
|
* this same command runs in far more often. CI and SSH override everything:
|
|
121
130
|
* both mean "no human is looking at this screen", whatever the platform says.
|
|
122
131
|
*/
|
|
123
|
-
function displayState(platform, env) {
|
|
132
|
+
function displayState(platform, env, wsl) {
|
|
124
133
|
if (env.CI) return { desktop: false, why: "CI is set" };
|
|
125
134
|
if (env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT) {
|
|
126
135
|
return { desktop: false, why: "this is an SSH session" };
|
|
@@ -128,14 +137,39 @@ function displayState(platform, env) {
|
|
|
128
137
|
if (platform === "win32") return { desktop: true, why: "Windows always has a session" };
|
|
129
138
|
if (platform === "darwin") return { desktop: true, why: "macOS always has a session" };
|
|
130
139
|
if (platform === "linux") {
|
|
140
|
+
// 7.79.1: WSL is server only, display or not. WSLg sets DISPLAY and
|
|
141
|
+
// WAYLAND_DISPLAY, so this used to install the LINUX desktop app inside
|
|
142
|
+
// WSL -- a second, separate install on a PC whose desktop app is the
|
|
143
|
+
// Windows one (and `mcc-desktop` refuses to run in WSL at all).
|
|
144
|
+
if (wsl ?? isWsl(platform, env)) {
|
|
145
|
+
return { desktop: false, why: "WSL, which gets the server only -- the desktop app for this PC is the Windows one" };
|
|
146
|
+
}
|
|
131
147
|
if (env.WAYLAND_DISPLAY) return { desktop: true, why: `WAYLAND_DISPLAY=${env.WAYLAND_DISPLAY}` };
|
|
132
148
|
if (env.DISPLAY) return { desktop: true, why: `DISPLAY=${env.DISPLAY}` };
|
|
133
|
-
|
|
134
|
-
return { desktop: false, why: wsl };
|
|
149
|
+
return { desktop: false, why: "no DISPLAY or WAYLAND_DISPLAY" };
|
|
135
150
|
}
|
|
136
151
|
return { desktop: false, why: `${platform} has no desktop build` };
|
|
137
152
|
}
|
|
138
153
|
|
|
154
|
+
/** The kernel file the product reads to recognise WSL (config/paths.py). */
|
|
155
|
+
const WSL_OSRELEASE_PATH = "/proc/sys/kernel/osrelease";
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Whether this Linux is WSL, by the product's own tests: the kernel's release
|
|
159
|
+
* string names Microsoft (config/paths.py, config/claude_discovery.py), or
|
|
160
|
+
* WSL_DISTRO_NAME / WSL_INTEROP are set (the sign-in flows' test). sudo's
|
|
161
|
+
* env_reset strips the two variables; it cannot strip the kernel string.
|
|
162
|
+
*/
|
|
163
|
+
function isWsl(platform, env) {
|
|
164
|
+
if (platform !== "linux") return false;
|
|
165
|
+
if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
|
|
166
|
+
try {
|
|
167
|
+
return /microsoft/i.test(fs.readFileSync(WSL_OSRELEASE_PATH, "utf8"));
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
139
173
|
/** The overrides present in `argv`, and any argument that is not one. */
|
|
140
174
|
function parseFlags(argv) {
|
|
141
175
|
const flags = new Set();
|
|
@@ -229,11 +263,15 @@ function decide(options) {
|
|
|
229
263
|
if (flags.has("--no-start")) serverFlags.push("--no-start");
|
|
230
264
|
else if (flags.has("--no-restart")) serverFlags.push("--no-restart");
|
|
231
265
|
if (!desktop && !flags.has("--no-start")) serverFlags.push("--no-desktop");
|
|
266
|
+
// install.sh's own sudo guard reads the same choice. install.ps1 has no
|
|
267
|
+
// such guard (and no such switch), so Windows never passes it.
|
|
268
|
+
if (flags.has("--allow-root") && platform !== "win32") serverFlags.push("--allow-root");
|
|
232
269
|
|
|
233
270
|
return {
|
|
234
271
|
server,
|
|
235
272
|
desktop,
|
|
236
273
|
serverFlags,
|
|
274
|
+
allowRoot: flags.has("--allow-root"),
|
|
237
275
|
// The server installer's `-Desktop`/`--desktop` flag adds the `mcc-desktop`
|
|
238
276
|
// launcher and its shortcuts. A headless box has nowhere to put them.
|
|
239
277
|
desktopFlag: desktop,
|
|
@@ -606,7 +644,59 @@ function serverInstallerCommand(platform, withDesktop, script, serverFlags) {
|
|
|
606
644
|
}
|
|
607
645
|
const posix = (withDesktop ? ["--desktop"] : []).concat(extras);
|
|
608
646
|
const flag = posix.length ? ` -s -- ${posix.join(" ")}` : "";
|
|
609
|
-
|
|
647
|
+
// 7.79.1: download first, then run -- never `curl ... | sh`. A pipeline
|
|
648
|
+
// exits with its LAST command's status, and `sh` fed an empty script exits
|
|
649
|
+
// 0, so a missing curl or a failed download was reported as a successful
|
|
650
|
+
// install ("done", with nothing installed) and `mcc uninstall` as a
|
|
651
|
+
// successful uninstall. Holding the whole script before running it also
|
|
652
|
+
// means a download cut off half way is never executed. The WARNING: prefix
|
|
653
|
+
// is what the npm hook's console filter lets through.
|
|
654
|
+
const url = `${REPO_RAW}/${name}.sh`;
|
|
655
|
+
const posixCommand =
|
|
656
|
+
`command -v curl >/dev/null 2>&1 || { echo "WARNING: curl is not installed, and it is what downloads ${name}.sh. ` +
|
|
657
|
+
`Install curl (for example: sudo apt install curl) and run this again." >&2; exit 127; }; ` +
|
|
658
|
+
`script=$(curl -fsSL "${url}") || { status=$?; echo "WARNING: could not download ${url} (curl exit $status). ` +
|
|
659
|
+
`Nothing was run. Check the network (and HTTPS_PROXY, if you use one) and run this again." >&2; exit $status; }; ` +
|
|
660
|
+
`[ -n "$script" ] || { echo "WARNING: ${url} came back empty. Nothing was run." >&2; exit 1; }; ` +
|
|
661
|
+
`printf '%s\\n' "$script" | sh${flag}`;
|
|
662
|
+
return { command: "sh", args: ["-c", posixCommand] };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* The user this run acts for when it is root through sudo, or null.
|
|
667
|
+
*
|
|
668
|
+
* 7.79.1. My Claude Code is a per-user install (uv's tool directory,
|
|
669
|
+
* ~/.local/bin, the config home), so `sudo npm install -g` -- which a system
|
|
670
|
+
* Node needs only to write the `mcc` command into a root-owned prefix --
|
|
671
|
+
* installed a second, root-owned copy for root and started a root server on
|
|
672
|
+
* the user's port (specs/INVESTIGATION-NPM-SUDO.md). Only root acting FOR a
|
|
673
|
+
* real user is refused: a root shell, a root VPS and a Docker image have no
|
|
674
|
+
* SUDO_USER (or SUDO_USER=root) and are unaffected.
|
|
675
|
+
*/
|
|
676
|
+
function sudoInvoker(platform, env, uid, allowRoot) {
|
|
677
|
+
if (platform === "win32" || uid !== 0) return null;
|
|
678
|
+
const user = env.SUDO_USER;
|
|
679
|
+
if (!user || user === "root") return null;
|
|
680
|
+
if (allowRoot || env.MCC_INSTALL_ALLOW_ROOT === "1") return null;
|
|
681
|
+
return user;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** This process's uid, or -1 where there is none (Windows). */
|
|
685
|
+
function currentUid() {
|
|
686
|
+
return typeof process.getuid === "function" ? process.getuid() : -1;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/** What a sudo run says instead of installing anything for root. */
|
|
690
|
+
function sudoRefusal(user) {
|
|
691
|
+
return (
|
|
692
|
+
`this is running as root through sudo, on behalf of ${user}, and My Claude Code installs per user. ` +
|
|
693
|
+
`As root it would install a second, root-owned copy that ${user} cannot use or update, and start a root-owned ` +
|
|
694
|
+
`server on ${user}'s port in place of ${user}'s own -- so nothing was installed for root. ` +
|
|
695
|
+
`The \`mcc\` command itself is in place: run \`mcc install\` (or just \`mcc\`) as ${user}, without sudo. ` +
|
|
696
|
+
`Next time, a user-level npm prefix needs no sudo at all: \`npm config set prefix ~/.npm-global\` ` +
|
|
697
|
+
"(and add ~/.npm-global/bin to PATH), then `npm install -g @firedmosquito831/my-claude-code`. " +
|
|
698
|
+
"To install for root on purpose: MCC_INSTALL_ALLOW_ROOT=1, or `mcc install --allow-root`."
|
|
699
|
+
);
|
|
610
700
|
}
|
|
611
701
|
|
|
612
702
|
/**
|
|
@@ -640,6 +730,21 @@ async function performInstall(options) {
|
|
|
640
730
|
|
|
641
731
|
// The one line the spec asks for: what, where, and why.
|
|
642
732
|
log(decision.reason);
|
|
733
|
+
// 7.79.1: nothing at all for root acting for another user -- no server and
|
|
734
|
+
// no desktop app. `npm install -g` passes `refusedStatus: 0`, so the `mcc`
|
|
735
|
+
// command it wrote (the only thing sudo was needed for) stays; `mcc
|
|
736
|
+
// install` and a bare `mcc` keep the default 1 and fail honestly.
|
|
737
|
+
const invoker = sudoInvoker(
|
|
738
|
+
platform,
|
|
739
|
+
options.env ?? process.env,
|
|
740
|
+
options.uid ?? currentUid(),
|
|
741
|
+
decision.allowRoot
|
|
742
|
+
);
|
|
743
|
+
if (invoker) {
|
|
744
|
+
log(sudoRefusal(invoker));
|
|
745
|
+
if (options.onRefused) options.onRefused(invoker);
|
|
746
|
+
return options.refusedStatus ?? 1;
|
|
747
|
+
}
|
|
643
748
|
if (!decision.server && !decision.desktop) {
|
|
644
749
|
log("nothing to do (MCC_NPM_INSTALL=none).");
|
|
645
750
|
return 0;
|
|
@@ -730,8 +835,12 @@ module.exports = {
|
|
|
730
835
|
displayState,
|
|
731
836
|
downloadVerified,
|
|
732
837
|
installDesktopFrom,
|
|
838
|
+
isWsl,
|
|
733
839
|
parseFlags,
|
|
734
840
|
parseSums,
|
|
735
841
|
sha256,
|
|
842
|
+
sudoInvoker,
|
|
843
|
+
sudoRefusal,
|
|
736
844
|
windowsInstallArgv,
|
|
845
|
+
WSL_OSRELEASE_PATH,
|
|
737
846
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firedmosquito831/my-claude-code",
|
|
3
|
-
"version": "7.79.
|
|
3
|
+
"version": "7.79.1",
|
|
4
4
|
"description": "My Claude Code (MCC): a local multi-provider LLM proxy, model router and control plane for Claude Code, Codex, OpenCode, Gemini CLI and other AI coding agents, with 57 providers, fallback routing, credential health, a native web-search tool proxy, and analytics with cost provenance. This npm package installs and launches the Python server.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"author": "FiredMosquito831",
|