@mcptoolshop/backpropagate 1.2.0 → 1.4.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.
- package/README.es.md +219 -207
- package/README.fr.md +219 -207
- package/README.hi.md +219 -207
- package/README.it.md +220 -208
- package/README.ja.md +219 -207
- package/README.md +220 -208
- package/README.pt-BR.md +219 -207
- package/README.zh.md +219 -207
- package/bin/backpropagate.js +47 -196
- package/package.json +2 -5
package/bin/backpropagate.js
CHANGED
|
@@ -1,204 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
const { spawnSync } = require("child_process");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const fs = require("fs");
|
|
7
|
-
const os = require("os");
|
|
8
|
-
|
|
9
4
|
// ---------------------------------------------------------------------------
|
|
10
|
-
//
|
|
5
|
+
// npm distribution of backpropagate is deprecated as of v1.3.
|
|
11
6
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
7
|
+
// History: v1.0–v1.2 shipped PyInstaller binaries from a GitHub Release,
|
|
8
|
+
// pulled via @mcptoolshop/npm-launcher. Linux bootstrapped a managed venv
|
|
9
|
+
// instead because libtorch_cpu.so blew past GitHub's 2GB release-asset cap.
|
|
10
|
+
// The binary build pipeline failed three consecutive times in v1.2.0 and the
|
|
11
|
+
// release tag has zero attached assets — the launcher would 404 on download.
|
|
16
12
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const installRoot = process.env.BACKPROPAGATE_BOOTSTRAP_ROOT
|
|
29
|
-
|| path.join(dataHome, TOOL);
|
|
30
|
-
const venvDir = path.join(installRoot, "venv");
|
|
31
|
-
const metaPath = path.join(installRoot, "install.json");
|
|
32
|
-
const venvBin = path.join(venvDir, "bin", TOOL);
|
|
33
|
-
const venvPython = path.join(venvDir, "bin", "python3");
|
|
34
|
-
|
|
35
|
-
// -- helpers --------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
function findPython() {
|
|
38
|
-
// Prefer python3, fall back to python (if it's 3.x)
|
|
39
|
-
for (const cmd of ["python3", "python"]) {
|
|
40
|
-
const r = spawnSync(cmd, ["--version"], {
|
|
41
|
-
encoding: "utf8",
|
|
42
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
43
|
-
});
|
|
44
|
-
if (r.status === 0) {
|
|
45
|
-
const ver = (r.stdout || r.stderr || "").trim();
|
|
46
|
-
const match = ver.match(/Python\s+(\d+)\.(\d+)/);
|
|
47
|
-
if (match && parseInt(match[1], 10) >= 3 && parseInt(match[2], 10) >= 10) {
|
|
48
|
-
return cmd;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function readMeta() {
|
|
56
|
-
try {
|
|
57
|
-
return JSON.parse(fs.readFileSync(metaPath, "utf8"));
|
|
58
|
-
} catch {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function writeMeta(obj) {
|
|
64
|
-
fs.mkdirSync(installRoot, { recursive: true });
|
|
65
|
-
fs.writeFileSync(metaPath, JSON.stringify(obj, null, 2) + "\n");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function fail(message, hint) {
|
|
69
|
-
process.stderr.write(`\n${TOOL}: ${message}\n`);
|
|
70
|
-
if (hint) process.stderr.write(`\n${hint}\n`);
|
|
71
|
-
process.stderr.write("\n");
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// -- bootstrap ------------------------------------------------------------
|
|
76
|
-
|
|
77
|
-
function bootstrap() {
|
|
78
|
-
const forceReinstall = process.env.BACKPROPAGATE_FORCE_REINSTALL === "1";
|
|
79
|
-
const meta = readMeta();
|
|
80
|
-
|
|
81
|
-
const versionMatch = meta && meta.version === VERSION;
|
|
82
|
-
const binaryPresent = fs.existsSync(venvBin);
|
|
83
|
-
|
|
84
|
-
if (versionMatch && binaryPresent && !forceReinstall) {
|
|
85
|
-
return; // fast path — venv exists, version matches, no forced reinstall
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Decide what to tell the user
|
|
89
|
-
if (forceReinstall) {
|
|
90
|
-
process.stderr.write(`Forced reinstall requested (BACKPROPAGATE_FORCE_REINSTALL=1).\n`);
|
|
91
|
-
} else if (meta && !versionMatch) {
|
|
92
|
-
process.stderr.write(
|
|
93
|
-
`Updating ${TOOL}: ${meta.version} -> ${VERSION}\n`
|
|
94
|
-
);
|
|
95
|
-
} else if (meta && !binaryPresent) {
|
|
96
|
-
process.stderr.write(
|
|
97
|
-
`Repairing ${TOOL}: binary missing, reinstalling ${VERSION}...\n`
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Find system Python
|
|
102
|
-
const python = findPython();
|
|
103
|
-
if (!python) {
|
|
104
|
-
fail(
|
|
105
|
-
"Python 3.10+ is required but not found.",
|
|
106
|
-
"Install Python and try again:\n" +
|
|
107
|
-
" Ubuntu/Debian: sudo apt install python3 python3-venv\n" +
|
|
108
|
-
" Fedora/RHEL: sudo dnf install python3\n" +
|
|
109
|
-
" Arch: sudo pacman -S python\n" +
|
|
110
|
-
" Or use pyenv: https://github.com/pyenv/pyenv"
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Recreate venv on force-reinstall (nuke corrupted state)
|
|
115
|
-
if (forceReinstall && fs.existsSync(venvDir)) {
|
|
116
|
-
process.stderr.write("Removing existing venv...\n");
|
|
117
|
-
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Create venv if missing
|
|
121
|
-
if (!fs.existsSync(venvPython)) {
|
|
122
|
-
if (!meta) {
|
|
123
|
-
process.stderr.write(
|
|
124
|
-
`First run on Linux: setting up local Python environment for ${TOOL}...\n`
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
fs.mkdirSync(installRoot, { recursive: true });
|
|
128
|
-
const venvResult = spawnSync(python, ["-m", "venv", venvDir], {
|
|
129
|
-
stdio: "inherit",
|
|
130
|
-
});
|
|
131
|
-
if (venvResult.status !== 0) {
|
|
132
|
-
fail(
|
|
133
|
-
"Failed to create Python virtual environment.",
|
|
134
|
-
"The venv module may be missing. Try:\n" +
|
|
135
|
-
" Ubuntu/Debian: sudo apt install python3-venv\n" +
|
|
136
|
-
" Fedora/RHEL: sudo dnf install python3-libs"
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Install or upgrade backpropagate
|
|
142
|
-
// --force-reinstall needed for repair (binary missing but pip metadata intact)
|
|
143
|
-
// and force-reinstall (nuked venv rebuilt). Without it, pip sees the same
|
|
144
|
-
// version in metadata and skips recreating the entrypoint script.
|
|
145
|
-
const needsForce = !binaryPresent || forceReinstall;
|
|
146
|
-
const pipArgs = ["-m", "pip", "install", "--quiet", "--upgrade"];
|
|
147
|
-
if (needsForce) pipArgs.push("--force-reinstall");
|
|
148
|
-
pipArgs.push(`${TOOL}==${VERSION}`);
|
|
149
|
-
|
|
150
|
-
process.stderr.write(`Installing ${TOOL} ${VERSION}${needsForce ? " (force)" : ""}...\n`);
|
|
151
|
-
const pipResult = spawnSync(venvPython, pipArgs, { stdio: "inherit" });
|
|
152
|
-
if (pipResult.status !== 0) {
|
|
153
|
-
fail(
|
|
154
|
-
`pip install failed (exit ${pipResult.status}).`,
|
|
155
|
-
"Check your network connection and try again.\n" +
|
|
156
|
-
"You can also install manually:\n" +
|
|
157
|
-
` pip install ${TOOL}==${VERSION}`
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Verify the binary actually exists after install
|
|
162
|
-
if (!fs.existsSync(venvBin)) {
|
|
163
|
-
fail(
|
|
164
|
-
`Installation completed but ${TOOL} binary not found at expected path.`,
|
|
165
|
-
`Expected: ${venvBin}\n` +
|
|
166
|
-
"Try installing manually:\n" +
|
|
167
|
-
` pipx install ${TOOL}`
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Record metadata
|
|
172
|
-
writeMeta({
|
|
173
|
-
version: VERSION,
|
|
174
|
-
installedAt: new Date().toISOString(),
|
|
175
|
-
python,
|
|
176
|
-
venvDir,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
process.stderr.write("Ready.\n");
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// -- exec -----------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
bootstrap();
|
|
185
|
-
const result = spawnSync(venvBin, process.argv.slice(2), { stdio: "inherit" });
|
|
186
|
-
if (result.error) {
|
|
187
|
-
fail(`Failed to execute ${TOOL}: ${result.error.message}`);
|
|
188
|
-
}
|
|
189
|
-
process.exit(result.status ?? 1);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// macOS + Windows: download and launch prebuilt binary via npm-launcher
|
|
13
|
+
// Rather than ship a broken installer, the npm shim now prints install
|
|
14
|
+
// guidance for the supported channels. The package stays published so this
|
|
15
|
+
// message reaches operators who still have `npm install -g backpropagate`
|
|
16
|
+
// in their tooling.
|
|
17
|
+
//
|
|
18
|
+
// Tracked: D2 SPLIT in the v1.3 brief. Wave 1 landed the friendly-error
|
|
19
|
+
// hotfix; Wave 3.5 deleted .github/workflows/release-binaries.yml; Wave 6a
|
|
20
|
+
// removed the PyInstaller .spec files at the repo root and added the v1.2.x
|
|
21
|
+
// → v1.3 handbook migration page that walks operators from the
|
|
22
|
+
// pre-deprecation `npm install -g backpropagate` install line to the
|
|
23
|
+
// supported channels. The migration is complete in v1.3.
|
|
194
24
|
// ---------------------------------------------------------------------------
|
|
195
25
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
26
|
+
// BRIDGE-B (Stage C humanization): the shim's WHOLE job is the friendly-error
|
|
27
|
+
// path. Three rules the message has to satisfy:
|
|
28
|
+
// 1. Name the next step (every error names the next step).
|
|
29
|
+
// 2. Make the install commands copy-paste-runnable on the operator's host
|
|
30
|
+
// (no shell quoting that fails on cmd.exe, no `sudo` prefix that
|
|
31
|
+
// misleads on Windows).
|
|
32
|
+
// 3. Stay calibrated — the v1.2 PyInstaller-bin build failed 3 times so
|
|
33
|
+
// v1.3 redirected operators to PyPI / pipx / uv; the message names the
|
|
34
|
+
// channels in preference order (isolated > shared > root-installable).
|
|
35
|
+
process.stderr.write(
|
|
36
|
+
"npm distribution of backpropagate is deprecated as of v1.3.\n" +
|
|
37
|
+
"\n" +
|
|
38
|
+
"Next step — install from PyPI via one of these channels (pick one):\n" +
|
|
39
|
+
" pipx install backpropagate # recommended on macOS/Linux (isolated venv, on PATH)\n" +
|
|
40
|
+
" uv tool install backpropagate # recommended on Windows (uv handles venv + PATH)\n" +
|
|
41
|
+
" pip install backpropagate # plain pip (use inside a venv)\n" +
|
|
42
|
+
"\n" +
|
|
43
|
+
"Verify the install: backprop --version\n" +
|
|
44
|
+
"\n" +
|
|
45
|
+
"Optional extras (Reflex UI, GGUF export, monitoring) — pick one bundle:\n" +
|
|
46
|
+
" pipx install 'backpropagate[standard]' # unsloth + ui (recommended)\n" +
|
|
47
|
+
" pipx install 'backpropagate[full]' # everything\n" +
|
|
48
|
+
" pipx install 'backpropagate[ui]' # just the Reflex web UI\n" +
|
|
49
|
+
"\n" +
|
|
50
|
+
"Getting started + extras documentation:\n" +
|
|
51
|
+
" https://mcp-tool-shop-org.github.io/backpropagate/handbook/getting-started/\n" +
|
|
52
|
+
"Source + issues:\n" +
|
|
53
|
+
" https://github.com/mcp-tool-shop-org/backpropagate\n"
|
|
54
|
+
);
|
|
55
|
+
process.exit(2);
|
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcptoolshop/backpropagate",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Deprecated npm distribution shim for backpropagate — prints install guidance for pipx/uv tool/pip. See https://github.com/mcp-tool-shop-org/backpropagate",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": { "node": ">=20" },
|
|
8
|
-
"dependencies": {
|
|
9
|
-
"@mcptoolshop/npm-launcher": "^1.0.0"
|
|
10
|
-
},
|
|
11
8
|
"bin": {
|
|
12
9
|
"backpropagate": "bin/backpropagate.js"
|
|
13
10
|
},
|