wuphf 0.12.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.md +37 -0
- package/bin/wuphf.js +46 -0
- package/package.json +44 -0
- package/scripts/download-binary.js +102 -0
- package/scripts/postinstall.js +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# wuphf
|
|
2
|
+
|
|
3
|
+
The pixel office CRM that reaches everyone, everywhere, all at once.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx wuphf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install globally:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g wuphf
|
|
15
|
+
wuphf
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Supported platforms: macOS and Linux on x64 or arm64.
|
|
19
|
+
|
|
20
|
+
## How it works
|
|
21
|
+
|
|
22
|
+
This package is a thin Node wrapper around the native `wuphf` Go binary.
|
|
23
|
+
On install (or on first run, if postinstall was skipped), it downloads
|
|
24
|
+
the matching release archive from
|
|
25
|
+
[github.com/nex-crm/wuphf/releases](https://github.com/nex-crm/wuphf/releases)
|
|
26
|
+
and places the binary in `node_modules/wuphf/bin/wuphf`.
|
|
27
|
+
|
|
28
|
+
To point the wrapper at a local build, set `WUPHF_BINARY`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
WUPHF_BINARY=./wuphf npx wuphf --version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Links
|
|
35
|
+
|
|
36
|
+
- Source: https://github.com/nex-crm/wuphf
|
|
37
|
+
- Issues: https://github.com/nex-crm/wuphf/issues
|
package/bin/wuphf.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Thin shim that spawns the native wuphf binary. Downloads lazily on first
|
|
5
|
+
// run if postinstall was skipped (common with `npm install --ignore-scripts`
|
|
6
|
+
// and with some `npx` cache behaviors).
|
|
7
|
+
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const { spawn } = require("node:child_process");
|
|
11
|
+
const { downloadBinary } = require("../scripts/download-binary");
|
|
12
|
+
|
|
13
|
+
const binaryPath =
|
|
14
|
+
process.env.WUPHF_BINARY || path.join(__dirname, "wuphf");
|
|
15
|
+
|
|
16
|
+
async function ensureBinary() {
|
|
17
|
+
if (process.env.WUPHF_BINARY && fs.existsSync(process.env.WUPHF_BINARY)) {
|
|
18
|
+
return process.env.WUPHF_BINARY;
|
|
19
|
+
}
|
|
20
|
+
if (fs.existsSync(binaryPath)) return binaryPath;
|
|
21
|
+
return downloadBinary();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run(resolvedPath) {
|
|
25
|
+
const child = spawn(resolvedPath, process.argv.slice(2), {
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
});
|
|
28
|
+
child.on("exit", (code, signal) => {
|
|
29
|
+
if (signal) {
|
|
30
|
+
process.kill(process.pid, signal);
|
|
31
|
+
} else {
|
|
32
|
+
process.exit(code ?? 0);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
child.on("error", (err) => {
|
|
36
|
+
process.stderr.write(`wuphf: failed to launch binary: ${err.message}\n`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ensureBinary()
|
|
42
|
+
.then(run)
|
|
43
|
+
.catch((err) => {
|
|
44
|
+
process.stderr.write(`wuphf: ${err.message}\n`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wuphf",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "WUPHF — pixel office CRM that reaches everyone, everywhere, all at once.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"wuphf": "bin/wuphf.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/wuphf.js",
|
|
10
|
+
"scripts/download-binary.js",
|
|
11
|
+
"scripts/postinstall.js",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"postinstall": "node scripts/postinstall.js"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"os": [
|
|
21
|
+
"darwin",
|
|
22
|
+
"linux"
|
|
23
|
+
],
|
|
24
|
+
"cpu": [
|
|
25
|
+
"x64",
|
|
26
|
+
"arm64"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/nex-crm/wuphf.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/nex-crm/wuphf",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/nex-crm/wuphf/issues"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"wuphf",
|
|
39
|
+
"crm",
|
|
40
|
+
"cli",
|
|
41
|
+
"pixel-office",
|
|
42
|
+
"nex"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Downloads the wuphf binary that matches the current package version
|
|
4
|
+
// from the corresponding GitHub release and extracts it into bin/.
|
|
5
|
+
// GoReleaser archive name: wuphf_<version>_<os>_<arch>.tar.gz
|
|
6
|
+
// where <version> is the tag without the leading 'v'.
|
|
7
|
+
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const fsp = require("node:fs/promises");
|
|
10
|
+
const path = require("node:path");
|
|
11
|
+
const os = require("node:os");
|
|
12
|
+
const { execFileSync } = require("node:child_process");
|
|
13
|
+
|
|
14
|
+
const REPO = "nex-crm/wuphf";
|
|
15
|
+
|
|
16
|
+
function detectPlatform() {
|
|
17
|
+
const platform = process.platform;
|
|
18
|
+
const arch = process.arch;
|
|
19
|
+
|
|
20
|
+
const osMap = { darwin: "darwin", linux: "linux" };
|
|
21
|
+
const archMap = { x64: "amd64", arm64: "arm64" };
|
|
22
|
+
|
|
23
|
+
if (!osMap[platform]) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Unsupported platform: ${platform}. wuphf supports darwin and linux.`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!archMap[arch]) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Unsupported architecture: ${arch}. wuphf supports x64 (amd64) and arm64.`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return { os: osMap[platform], arch: archMap[arch] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function packageVersion() {
|
|
37
|
+
const pkg = JSON.parse(
|
|
38
|
+
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"),
|
|
39
|
+
);
|
|
40
|
+
return pkg.version;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function archiveUrl(version) {
|
|
44
|
+
const { os: goOs, arch: goArch } = detectPlatform();
|
|
45
|
+
const archive = `wuphf_${version}_${goOs}_${goArch}.tar.gz`;
|
|
46
|
+
return `https://github.com/${REPO}/releases/download/v${version}/${archive}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function fetchToFile(url, dest) {
|
|
50
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(`Download failed: ${res.status} ${res.statusText} (${url})`);
|
|
53
|
+
}
|
|
54
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
55
|
+
await fsp.writeFile(dest, buf);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function downloadBinary({ silent = false } = {}) {
|
|
59
|
+
const version = packageVersion();
|
|
60
|
+
const url = archiveUrl(version);
|
|
61
|
+
const binDir = path.join(__dirname, "..", "bin");
|
|
62
|
+
const binaryPath = path.join(binDir, "wuphf");
|
|
63
|
+
|
|
64
|
+
await fsp.mkdir(binDir, { recursive: true });
|
|
65
|
+
|
|
66
|
+
const tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), "wuphf-"));
|
|
67
|
+
const archivePath = path.join(tmpDir, "wuphf.tar.gz");
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
if (!silent) {
|
|
71
|
+
process.stderr.write(`wuphf: downloading ${url}\n`);
|
|
72
|
+
}
|
|
73
|
+
await fetchToFile(url, archivePath);
|
|
74
|
+
|
|
75
|
+
// Extract using system tar (available on darwin + linux).
|
|
76
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], {
|
|
77
|
+
stdio: silent ? "ignore" : "inherit",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const extractedBinary = path.join(tmpDir, "wuphf");
|
|
81
|
+
await fsp.copyFile(extractedBinary, binaryPath);
|
|
82
|
+
await fsp.chmod(binaryPath, 0o755);
|
|
83
|
+
|
|
84
|
+
// macOS 15+ invalidates GoReleaser's embedded ad-hoc signature after
|
|
85
|
+
// copy+chmod. Re-sign so the kernel does not SIGKILL on exec.
|
|
86
|
+
if (process.platform === "darwin") {
|
|
87
|
+
try {
|
|
88
|
+
execFileSync("codesign", ["--force", "--sign", "-", binaryPath], {
|
|
89
|
+
stdio: "ignore",
|
|
90
|
+
});
|
|
91
|
+
} catch {
|
|
92
|
+
// codesign is optional — binary may still run.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return binaryPath;
|
|
97
|
+
} finally {
|
|
98
|
+
await fsp.rm(tmpDir, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { downloadBinary, packageVersion };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Best-effort: fetch the binary at install time. Failures are non-fatal —
|
|
4
|
+
// the bin/wuphf.js shim will retry on first invocation. This keeps
|
|
5
|
+
// `npm install` from failing behind flaky networks or corporate proxies.
|
|
6
|
+
|
|
7
|
+
const { downloadBinary } = require("./download-binary");
|
|
8
|
+
|
|
9
|
+
if (process.env.WUPHF_SKIP_POSTINSTALL === "1") {
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
downloadBinary().catch((err) => {
|
|
14
|
+
process.stderr.write(
|
|
15
|
+
`wuphf: postinstall download failed (${err.message}). ` +
|
|
16
|
+
`The binary will be fetched on first run.\n`,
|
|
17
|
+
);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
});
|