wuphf 0.83.15 → 0.85.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 CHANGED
@@ -35,7 +35,7 @@ Prefer a global install?
35
35
  npm install -g wuphf && wuphf
36
36
  ```
37
37
 
38
- Supported platforms: macOS and Linux on x64 or arm64. The native binary is lazy-downloaded from [GitHub releases](https://github.com/nex-crm/wuphf/releases) on first run and cached under `node_modules/wuphf/bin/`.
38
+ Supported platforms: macOS, Linux, and Windows 10+ on x64 or arm64. The native binary is lazy-downloaded from [GitHub releases](https://github.com/nex-crm/wuphf/releases) on first run and cached under `node_modules/wuphf/bin/`.
39
39
 
40
40
  > **Stability:** pre-1.0. `main` moves daily. Pin to a release tag, not `main`.
41
41
 
package/bin/wuphf.js CHANGED
@@ -29,15 +29,18 @@ const { spawn } = require("node:child_process");
29
29
  const { downloadBinary, packageVersion } = require("../scripts/download-binary");
30
30
  const { getLatestVersion, compareVersions } = require("../scripts/version-check");
31
31
 
32
- const installedBinary = path.join(__dirname, "wuphf");
32
+ const binaryName = process.platform === "win32" ? "wuphf.exe" : "wuphf";
33
+ const installedBinary = path.join(__dirname, binaryName);
33
34
 
34
35
  function cachedBinaryPath(version) {
36
+ // Windows requires the .exe suffix or CreateProcess refuses to launch.
37
+ const suffix = process.platform === "win32" ? ".exe" : "";
35
38
  return path.join(
36
39
  os.homedir(),
37
40
  ".wuphf",
38
41
  "cache",
39
42
  "binaries",
40
- `wuphf-${version}`,
43
+ `wuphf-${version}${suffix}`,
41
44
  );
42
45
  }
43
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wuphf",
3
- "version": "0.83.15",
3
+ "version": "0.85.0",
4
4
  "description": "Slack for AI employees with a shared brain. A collaborative office where AI employees run your work 24x7.",
5
5
  "bin": {
6
6
  "wuphf": "bin/wuphf.js"
@@ -13,14 +13,16 @@
13
13
  "README.md"
14
14
  ],
15
15
  "scripts": {
16
- "postinstall": "node scripts/postinstall.js"
16
+ "postinstall": "node scripts/postinstall.js",
17
+ "test": "bun test"
17
18
  },
18
19
  "engines": {
19
20
  "node": ">=18"
20
21
  },
21
22
  "os": [
22
23
  "darwin",
23
- "linux"
24
+ "linux",
25
+ "win32"
24
26
  ],
25
27
  "cpu": [
26
28
  "x64",
@@ -45,12 +45,12 @@ function detectPlatform() {
45
45
  const platform = process.platform;
46
46
  const arch = process.arch;
47
47
 
48
- const osMap = { darwin: "darwin", linux: "linux" };
48
+ const osMap = { darwin: "darwin", linux: "linux", win32: "windows" };
49
49
  const archMap = { x64: "amd64", arm64: "arm64" };
50
50
 
51
51
  if (!osMap[platform]) {
52
52
  throw new Error(
53
- `Unsupported platform: ${platform}. wuphf supports darwin and linux.`,
53
+ `Unsupported platform: ${platform}. wuphf supports darwin, linux, and windows.`,
54
54
  );
55
55
  }
56
56
  if (!archMap[arch]) {
@@ -68,9 +68,21 @@ function packageVersion() {
68
68
  return pkg.version;
69
69
  }
70
70
 
71
+ // goreleaser packages Windows builds as .zip and everything else as
72
+ // .tar.gz (see .goreleaser.yml format_overrides). Stay consistent with
73
+ // what's actually published.
74
+ function archiveExtension() {
75
+ return process.platform === "win32" ? "zip" : "tar.gz";
76
+ }
77
+
71
78
  function archiveName(version) {
72
79
  const { os: goOs, arch: goArch } = detectPlatform();
73
- return `wuphf_${version}_${goOs}_${goArch}.tar.gz`;
80
+ return `wuphf_${version}_${goOs}_${goArch}.${archiveExtension()}`;
81
+ }
82
+
83
+ // Go names Windows binaries with a .exe suffix; the rest get the bare name.
84
+ function binaryFilename() {
85
+ return process.platform === "win32" ? "wuphf.exe" : "wuphf";
74
86
  }
75
87
 
76
88
  function releaseAssetUrl(version, filename) {
@@ -171,11 +183,34 @@ async function verifyArchive({ version, archivePath, archiveBasename, silent })
171
183
  // targetPath — where to place the extracted binary. Defaults to
172
184
  // bin/wuphf inside this package. The out-of-tree cache uses
173
185
  // a version-keyed path so multiple versions can coexist.
186
+ // Extract a goreleaser archive into tmpDir. Picks the right tool by
187
+ // extension: tar for .tar.gz (built into macOS + Linux), and PowerShell
188
+ // Expand-Archive on Windows because Node has no built-in unzip and we
189
+ // don't want to drag in a dependency. Windows 10 1803+ ships with
190
+ // `tar.exe` (bsdtar) which can also extract .zip — we prefer that on
191
+ // Windows because it doesn't need PowerShell's slower startup.
192
+ function extractArchive(archivePath, tmpDir, silent) {
193
+ const stdio = silent ? "ignore" : "inherit";
194
+ if (archivePath.endsWith(".zip")) {
195
+ if (process.platform === "win32") {
196
+ // Windows 10 1803+ has tar.exe (bsdtar). It accepts .zip natively.
197
+ execFileSync("tar", ["-xf", archivePath, "-C", tmpDir], { stdio });
198
+ return;
199
+ }
200
+ // macOS / Linux fallback: use unzip if available.
201
+ execFileSync("unzip", ["-q", "-o", archivePath, "-d", tmpDir], { stdio });
202
+ return;
203
+ }
204
+ // Default: gzipped tar. Works on darwin + linux.
205
+ execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio });
206
+ }
207
+
174
208
  async function downloadBinary({ silent = false, version, targetPath } = {}) {
175
209
  const resolvedVersion = version ?? packageVersion();
176
210
  const archiveBasename = archiveName(resolvedVersion);
177
211
  const url = releaseAssetUrl(resolvedVersion, archiveBasename);
178
- const binaryPath = targetPath ?? path.join(__dirname, "..", "bin", "wuphf");
212
+ const binaryName = binaryFilename();
213
+ const binaryPath = targetPath ?? path.join(__dirname, "..", "bin", binaryName);
179
214
  const binDir = path.dirname(binaryPath);
180
215
 
181
216
  await fsp.mkdir(binDir, { recursive: true });
@@ -197,14 +232,16 @@ async function downloadBinary({ silent = false, version, targetPath } = {}) {
197
232
  silent,
198
233
  });
199
234
 
200
- // Extract using system tar (available on darwin + linux).
201
- execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], {
202
- stdio: silent ? "ignore" : "inherit",
203
- });
235
+ extractArchive(archivePath, tmpDir, silent);
204
236
 
205
- const extractedBinary = path.join(tmpDir, "wuphf");
237
+ const extractedBinary = path.join(tmpDir, binaryName);
206
238
  await fsp.copyFile(extractedBinary, binaryPath);
207
- await fsp.chmod(binaryPath, 0o755);
239
+
240
+ if (process.platform !== "win32") {
241
+ // chmod is a no-op on Windows (NTFS doesn't track the +x bit) and
242
+ // calling it just emits a warning on some Node versions.
243
+ await fsp.chmod(binaryPath, 0o755);
244
+ }
208
245
 
209
246
  // macOS 15+ invalidates GoReleaser's embedded ad-hoc signature after
210
247
  // copy+chmod. Re-sign so the kernel does not SIGKILL on exec.
@@ -230,4 +267,8 @@ module.exports = {
230
267
  // Exported for tests.
231
268
  expectedHashFor,
232
269
  sha256OfFile,
270
+ detectPlatform,
271
+ archiveExtension,
272
+ archiveName,
273
+ binaryFilename,
233
274
  };