@bitmilldev/warden 1.0.2 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitmilldev/warden",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "AI Coding Session Guardian — runtime intelligence layer for Claude Code, Gemini CLI, and more",
5
5
  "keywords": [
6
6
  "ai",
@@ -2,7 +2,7 @@
2
2
  // Warden npm/bun postinstall — downloads the platform-specific binary
3
3
  // to ~/.warden/bin/ and registers PATH.
4
4
 
5
- const { execSync, spawnSync } = require("child_process");
5
+ const { spawnSync } = require("child_process");
6
6
  const fs = require("fs");
7
7
  const path = require("path");
8
8
  const os = require("os");
@@ -32,57 +32,129 @@ async function main() {
32
32
 
33
33
  const wardenHome = path.join(os.homedir(), ".warden");
34
34
  const binDir = path.join(wardenHome, "bin");
35
- const destName = os.platform() === "win32" ? "warden.exe" : "warden";
36
- const dest = path.join(binDir, destName);
35
+ const ext = os.platform() === "win32" ? ".exe" : "";
36
+ const dest = path.join(binDir, `warden${ext}`);
37
37
 
38
38
  // Create directories
39
39
  fs.mkdirSync(binDir, { recursive: true });
40
40
  fs.mkdirSync(path.join(wardenHome, "rules"), { recursive: true });
41
41
  fs.mkdirSync(path.join(wardenHome, "projects"), { recursive: true });
42
42
 
43
- // Download binary from GitHub Releases
43
+ // Strategy 1: Check if warden is already installed via cargo and is current version
44
+ const localCargo = findCargoBinary();
45
+ if (localCargo) {
46
+ console.log(`Found cargo-installed warden at ${localCargo}`);
47
+ if (localCargo !== dest) {
48
+ fs.copyFileSync(localCargo, dest);
49
+ console.log(`Copied to ${dest}`);
50
+ }
51
+ copyRelay(binDir, localCargo);
52
+ postInstall(dest);
53
+ return;
54
+ }
55
+
56
+ // Strategy 2: Download from GitHub Releases
44
57
  const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${binary}`;
45
58
  console.log(`Downloading warden v${VERSION} for ${platform}...`);
46
59
 
47
60
  try {
48
61
  await download(url, dest);
62
+ const stat = fs.statSync(dest);
63
+ if (stat.size === 0) {
64
+ fs.unlinkSync(dest);
65
+ throw new Error("Downloaded file is empty (asset may not exist in release)");
66
+ }
67
+
49
68
  if (os.platform() === "win32") {
50
- // Remove Zone.Identifier to prevent SmartScreen "Access denied"
51
69
  try { fs.unlinkSync(dest + ":Zone.Identifier"); } catch (e) {}
52
70
  } else {
53
71
  fs.chmodSync(dest, 0o755);
54
72
  }
55
73
 
56
- // Also download the relay binary (Windows only — prevents CMD flicker)
74
+ // Download relay binary (Windows only — prevents CMD flicker)
57
75
  if (os.platform() === "win32") {
58
76
  const relayBinary = binary.replace("warden-", "warden-relay-");
59
77
  const relayDest = path.join(binDir, "warden-relay.exe");
60
78
  const relayUrl = `https://github.com/${REPO}/releases/download/v${VERSION}/${relayBinary}`;
61
79
  try {
62
80
  await download(relayUrl, relayDest);
63
- try { fs.unlinkSync(relayDest + ":Zone.Identifier"); } catch (e) {}
64
- console.log(`Installed relay to ${relayDest}`);
81
+ const relayStat = fs.statSync(relayDest);
82
+ if (relayStat.size === 0) {
83
+ fs.unlinkSync(relayDest);
84
+ } else {
85
+ try { fs.unlinkSync(relayDest + ":Zone.Identifier"); } catch (e) {}
86
+ console.log(`Installed relay to ${relayDest}`);
87
+ }
65
88
  } catch (e) {
66
89
  // Relay is optional — warden works without it (just has CMD flicker)
67
90
  }
68
91
  }
69
92
 
70
- console.log(`Installed to ${dest}`);
93
+ postInstall(dest);
94
+ } catch (err) {
95
+ // Clean up empty/partial file
96
+ try { if (fs.existsSync(dest) && fs.statSync(dest).size === 0) fs.unlinkSync(dest); } catch (e) {}
97
+
98
+ console.error(`Download failed: ${err.message}`);
99
+ console.error("");
100
+ console.error("Alternative install methods:");
101
+ console.error(" cargo install warden-ai");
102
+ console.error(" Download from: https://github.com/ekud12/warden/releases");
103
+ process.exit(0); // Don't fail npm install
104
+ }
105
+ }
106
+
107
+ function postInstall(dest) {
108
+ console.log(`Installed to ${dest}`);
109
+
110
+ // Verify binary works
111
+ try {
112
+ spawnSync(dest, ["version"], { stdio: "inherit", timeout: 5000 });
113
+ } catch (e) {
114
+ // Ignore — binary might need different setup
115
+ }
116
+
117
+ console.log("");
118
+ console.log("Run 'warden init' to complete setup (install CLI tools, configure hooks).");
119
+ console.log("Or: 'warden install claude-code' / 'warden install gemini-cli'");
120
+ }
121
+
122
+ function findCargoBinary() {
123
+ // Check common cargo install locations
124
+ const ext = os.platform() === "win32" ? ".exe" : "";
125
+ const candidates = [
126
+ path.join(os.homedir(), ".cargo", "bin", `warden${ext}`),
127
+ ];
128
+
129
+ // Also check if there's a local build in the project (for development)
130
+ const envBinary = process.env.WARDEN_BINARY;
131
+ if (envBinary && fs.existsSync(envBinary)) {
132
+ return envBinary;
133
+ }
134
+
135
+ for (const candidate of candidates) {
136
+ if (fs.existsSync(candidate)) {
137
+ const stat = fs.statSync(candidate);
138
+ if (stat.size > 0) return candidate;
139
+ }
140
+ }
141
+ return null;
142
+ }
143
+
144
+ function copyRelay(binDir, sourceBinaryDir) {
145
+ if (os.platform() !== "win32") return;
146
+
147
+ const sourceDir = path.dirname(sourceBinaryDir);
148
+ const relaySource = path.join(sourceDir, "warden-relay.exe");
149
+ const relayDest = path.join(binDir, "warden-relay.exe");
71
150
 
72
- // Run warden init (non-interactive: just PATH + config)
151
+ if (fs.existsSync(relaySource) && relaySource !== relayDest) {
73
152
  try {
74
- spawnSync(dest, ["version"], { stdio: "inherit" });
153
+ fs.copyFileSync(relaySource, relayDest);
154
+ console.log(`Copied relay to ${relayDest}`);
75
155
  } catch (e) {
76
- // Ignore — binary might need different setup
156
+ // Non-fatal
77
157
  }
78
-
79
- console.log("");
80
- console.log("Run 'warden init' to complete setup (install CLI tools, configure hooks).");
81
- console.log(`Or: 'warden install claude-code' / 'warden install gemini-cli'`);
82
- } catch (err) {
83
- console.error(`Download failed: ${err.message}`);
84
- console.error("Install from source: cargo install warden-ai");
85
- process.exit(0); // Don't fail npm install
86
158
  }
87
159
  }
88
160
 
@@ -92,7 +164,7 @@ function download(url, dest, redirects = 0) {
92
164
  const proto = url.startsWith("https") ? https : require("http");
93
165
  proto.get(url, (response) => {
94
166
  if (response.statusCode === 301 || response.statusCode === 302) {
95
- response.resume(); // drain the response
167
+ response.resume();
96
168
  return resolve(download(response.headers.location, dest, redirects + 1));
97
169
  }
98
170
  if (response.statusCode !== 200) {
@@ -102,8 +174,14 @@ function download(url, dest, redirects = 0) {
102
174
  const file = fs.createWriteStream(dest);
103
175
  response.pipe(file);
104
176
  file.on("finish", () => { file.close(); resolve(); });
105
- file.on("error", (e) => { fs.unlinkSync(dest); reject(e); });
106
- }).on("error", reject);
177
+ file.on("error", (e) => {
178
+ try { fs.unlinkSync(dest); } catch (ignore) {}
179
+ reject(e);
180
+ });
181
+ }).on("error", (e) => {
182
+ try { fs.unlinkSync(dest); } catch (ignore) {}
183
+ reject(e);
184
+ });
107
185
  });
108
186
  }
109
187