wuphf 0.216.1 → 0.217.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/bin/wuphf.js CHANGED
@@ -27,7 +27,7 @@ const path = require("node:path");
27
27
  const os = require("node:os");
28
28
  const { spawn } = require("node:child_process");
29
29
  const { downloadBinary, packageVersion } = require("../scripts/download-binary");
30
- const { getLatestVersion, compareVersions } = require("../scripts/version-check");
30
+ const { getLatestVersion, compareVersions, ensureCacheDir } = require("../scripts/version-check");
31
31
 
32
32
  const binaryName = process.platform === "win32" ? "wuphf.exe" : "wuphf";
33
33
  const installedBinary = path.join(__dirname, binaryName);
@@ -35,8 +35,9 @@ const installedBinary = path.join(__dirname, binaryName);
35
35
  function cachedBinaryPath(version) {
36
36
  // Windows requires the .exe suffix or CreateProcess refuses to launch.
37
37
  const suffix = process.platform === "win32" ? ".exe" : "";
38
+ const runtimeHome = process.env.WUPHF_RUNTIME_HOME?.trim() || os.homedir();
38
39
  return path.join(
39
- os.homedir(),
40
+ runtimeHome,
40
41
  ".wuphf",
41
42
  "cache",
42
43
  "binaries",
@@ -70,6 +71,7 @@ async function ensureBinary() {
70
71
  const cachedPath = cachedBinaryPath(latestVersion);
71
72
  if (!fs.existsSync(cachedPath)) {
72
73
  try {
74
+ await ensureCacheDir();
73
75
  await downloadBinary({
74
76
  version: latestVersion,
75
77
  targetPath: cachedPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wuphf",
3
- "version": "0.216.1",
3
+ "version": "0.217.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"
@@ -24,6 +24,7 @@ const fs = require("node:fs");
24
24
  const fsp = require("node:fs/promises");
25
25
  const path = require("node:path");
26
26
  const os = require("node:os");
27
+ const { execFile } = require("node:child_process");
27
28
 
28
29
  const REGISTRY_URL = "https://registry.npmjs.org/wuphf/latest";
29
30
  // Generous enough to survive a cold TLS handshake on a slow network but
@@ -31,17 +32,57 @@ const REGISTRY_URL = "https://registry.npmjs.org/wuphf/latest";
31
32
  // per user because the result is cached on disk.
32
33
  const FETCH_TIMEOUT_MS = 3000;
33
34
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
35
+ const backupExcludedCacheDirs = new Set();
36
+ let backupExclusionRunner = (target) =>
37
+ new Promise((resolve, reject) => {
38
+ execFile(
39
+ "/usr/bin/tmutil",
40
+ ["addexclusion", target],
41
+ { timeout: 2000 },
42
+ (err) => {
43
+ if (err) reject(err);
44
+ else resolve();
45
+ },
46
+ );
47
+ });
34
48
 
35
49
  function cacheDir() {
36
- // Sits under ~/.wuphf so HOME-override dev environments (see
37
- // docs/LOCAL-DEV-PROD-ISOLATION.md) get a separate cache from prod.
38
- return path.join(os.homedir(), ".wuphf", "cache");
50
+ // Sits under the active WUPHF runtime home so workspace/dev overrides get
51
+ // separate caches from prod.
52
+ const runtimeHome = process.env.WUPHF_RUNTIME_HOME?.trim() || os.homedir();
53
+ return path.join(runtimeHome, ".wuphf", "cache");
39
54
  }
40
55
 
41
56
  function latestVersionCachePath() {
42
57
  return path.join(cacheDir(), "latest-version.json");
43
58
  }
44
59
 
60
+ async function markCacheDirExcludedFromBackup(dir = cacheDir()) {
61
+ if (process.platform !== "darwin") return;
62
+ const candidates = new Set([dir]);
63
+ try {
64
+ candidates.add(await fsp.realpath(dir));
65
+ } catch {
66
+ // The cache write path is best-effort; backup metadata is too.
67
+ }
68
+ for (const candidate of candidates) {
69
+ if (backupExcludedCacheDirs.has(candidate)) continue;
70
+ backupExcludedCacheDirs.add(candidate);
71
+ try {
72
+ await backupExclusionRunner(candidate);
73
+ } catch {
74
+ backupExcludedCacheDirs.delete(candidate);
75
+ }
76
+ }
77
+ }
78
+
79
+ async function ensureCacheDir() {
80
+ const dir = cacheDir();
81
+ await fsp.mkdir(dir, { recursive: true, mode: 0o700 });
82
+ await markCacheDirExcludedFromBackup(dir);
83
+ return dir;
84
+ }
85
+
45
86
  async function readCache() {
46
87
  try {
47
88
  const raw = await fsp.readFile(latestVersionCachePath(), "utf8");
@@ -59,7 +100,7 @@ async function readCache() {
59
100
 
60
101
  async function writeCache(version) {
61
102
  try {
62
- await fsp.mkdir(cacheDir(), { recursive: true });
103
+ await ensureCacheDir();
63
104
  const target = latestVersionCachePath();
64
105
  const tmp = `${target}.tmp`;
65
106
  await fsp.writeFile(tmp, JSON.stringify({ version, checkedAt: Date.now() }));
@@ -119,9 +160,15 @@ module.exports = {
119
160
  getLatestVersion,
120
161
  compareVersions,
121
162
  cacheDir,
163
+ ensureCacheDir,
122
164
  latestVersionCachePath,
123
165
  // Exported for tests.
166
+ markCacheDirExcludedFromBackup,
124
167
  fetchLatestFromRegistry,
125
168
  readCache,
126
169
  writeCache,
170
+ setBackupExclusionRunnerForTest(runner) {
171
+ backupExcludedCacheDirs.clear();
172
+ backupExclusionRunner = runner;
173
+ },
127
174
  };