@profullstack/threatcrush 0.1.14 → 0.1.15

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.
Files changed (3) hide show
  1. package/README.md +26 -4
  2. package/package.json +1 -1
  3. package/src/index.ts +101 -37
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <a href="https://threatcrush.com"><img src="https://threatcrush.com/logo.svg" alt="ThreatCrush" width="120" /></a>
3
+ </p>
4
+
1
5
  <p align="center">
2
6
  <a href="https://www.npmjs.com/package/@profullstack/threatcrush"><img src="https://img.shields.io/npm/v/@profullstack/threatcrush?color=00ff41&style=flat-square&label=version" alt="npm version" /></a>
3
7
  <a href="https://www.npmjs.com/package/@profullstack/threatcrush"><img src="https://img.shields.io/npm/dm/@profullstack/threatcrush?color=00ff41&style=flat-square&label=downloads" alt="downloads" /></a>
@@ -10,7 +14,7 @@
10
14
 
11
15
  <h1 align="center">
12
16
  <br>
13
- 🛡️ ThreatCrush
17
+ ThreatCrush
14
18
  <br>
15
19
  </h1>
16
20
 
@@ -43,15 +47,33 @@ $ threatcrush monitor
43
47
 
44
48
  ## Install
45
49
 
50
+ **Preferred install:**
51
+
46
52
  ```bash
47
- npm i -g @profullstack/threatcrush
53
+ curl -fsSL https://threatcrush.com/install.sh | sh
54
+ ```
55
+
56
+ The installer detects whether the machine is a server or desktop, uses your existing package manager when available, and can bootstrap Node.js with `mise` on bare machines.
57
+
58
+ - **Linux server** → installs the CLI
59
+ - **Linux desktop** → installs the CLI + desktop app
60
+ - **Windows desktop** → installs the desktop app to connect to a ThreatCrush server elsewhere
61
+ - **macOS desktop** → desktop-oriented install for connecting to a ThreatCrush server
62
+
63
+ After install, the supported lifecycle commands are:
64
+
65
+ ```bash
66
+ threatcrush update # upgrades the installed bundle
67
+ threatcrush remove # removes the installed bundle
48
68
  ```
49
69
 
50
- Or with your preferred package manager:
70
+ Manual package-manager installs still work:
51
71
 
52
72
  ```bash
73
+ npm i -g @profullstack/threatcrush
53
74
  pnpm add -g @profullstack/threatcrush
54
75
  yarn global add @profullstack/threatcrush
76
+ bun add -g @profullstack/threatcrush
55
77
  ```
56
78
 
57
79
  ## Usage
@@ -66,7 +88,7 @@ threatcrush init # Auto-detect services, generate config
66
88
  threatcrush status # Show daemon status & loaded modules
67
89
  threatcrush modules # Manage security modules
68
90
  threatcrush store # Browse the module marketplace
69
- threatcrush update # Update CLI & all modules
91
+ threatcrush update # Upgrade the CLI using the supported path
70
92
  ```
71
93
 
72
94
  ## Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/threatcrush",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "All-in-one security agent daemon — monitor, detect, scan, and protect servers in real-time",
5
5
  "bin": {
6
6
  "threatcrush": "./dist/index.js"
package/src/index.ts CHANGED
@@ -26,6 +26,8 @@ ${chalk.dim(" C R U S H")}
26
26
 
27
27
  const API_URL = process.env.THREATCRUSH_API_URL || "https://threatcrush.com";
28
28
  const PKG_NAME = "@profullstack/threatcrush";
29
+ const DESKTOP_PKG_NAME = "@profullstack/threatcrush-desktop";
30
+ const INSTALL_CONFIG_PATH = join(homedir(), ".threatcrush", "install.json");
29
31
 
30
32
  // ─── Helpers ───
31
33
 
@@ -96,6 +98,61 @@ function detectPackageManager(): string {
96
98
  return "npm";
97
99
  }
98
100
 
101
+ function readInstallConfig(): { installMode?: string; packageManager?: string; installMethod?: string } {
102
+ try {
103
+ return JSON.parse(readFileSync(INSTALL_CONFIG_PATH, "utf-8"));
104
+ } catch {
105
+ return {};
106
+ }
107
+ }
108
+
109
+ function getGlobalInstallCommand(pm: string, pkgName: string, action: "install" | "update" | "remove"): string {
110
+ const commands: Record<string, Record<string, string>> = {
111
+ npm: {
112
+ install: `npm i -g ${pkgName}`,
113
+ update: `npm update -g ${pkgName}`,
114
+ remove: `npm uninstall -g ${pkgName}`,
115
+ },
116
+ pnpm: {
117
+ install: `pnpm add -g ${pkgName}`,
118
+ update: `pnpm update -g ${pkgName}`,
119
+ remove: `pnpm remove -g ${pkgName}`,
120
+ },
121
+ yarn: {
122
+ install: `yarn global add ${pkgName}`,
123
+ update: `yarn global upgrade ${pkgName}`,
124
+ remove: `yarn global remove ${pkgName}`,
125
+ },
126
+ bun: {
127
+ install: `bun add -g ${pkgName}`,
128
+ update: `bun update -g ${pkgName}`,
129
+ remove: `bun remove -g ${pkgName}`,
130
+ },
131
+ };
132
+
133
+ return commands[pm]?.[action] || commands.npm[action];
134
+ }
135
+
136
+ function packageLooksInstalled(pm: string, pkgName: string): boolean {
137
+ try {
138
+ const listCommands: Record<string, string> = {
139
+ npm: `npm ls -g ${pkgName} --depth=0`,
140
+ pnpm: `pnpm list -g ${pkgName} --depth=0`,
141
+ yarn: `yarn global list --pattern ${pkgName}`,
142
+ bun: `bun pm ls -g`,
143
+ };
144
+
145
+ const output = execSync(listCommands[pm] || listCommands.npm, {
146
+ encoding: "utf-8",
147
+ stdio: ["pipe", "pipe", "pipe"],
148
+ });
149
+
150
+ return output.includes(pkgName);
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+
99
156
  // ─── Program ───
100
157
 
101
158
  const program = new Command();
@@ -167,9 +224,10 @@ gatedCommand("activate", "Activate your license key");
167
224
 
168
225
  program
169
226
  .command("update")
170
- .description("Update ThreatCrush CLI and all installed modules")
227
+ .description("Update ThreatCrush CLI and installed bundle")
171
228
  .option("--cli", "Update CLI only")
172
229
  .option("--modules", "Update modules only")
230
+ .option("--desktop", "Update desktop app too")
173
231
  .action(async (opts) => {
174
232
  console.log(LOGO);
175
233
 
@@ -179,42 +237,42 @@ program
179
237
  }
180
238
 
181
239
  const pm = detectPackageManager();
182
- console.log(chalk.dim(` Detected package manager: ${pm}\n`));
240
+ const installConfig = readInstallConfig();
241
+ const installMode = opts.cli ? "server" : (opts.desktop ? "desktop" : installConfig.installMode || "server");
183
242
 
184
- const commands: Record<string, string> = {
185
- npm: `npm update -g ${PKG_NAME}`,
186
- pnpm: `pnpm update -g ${PKG_NAME}`,
187
- yarn: `yarn global upgrade ${PKG_NAME}`,
188
- bun: `bun update -g ${PKG_NAME}`,
189
- };
243
+ console.log(chalk.dim(` Detected package manager: ${pm}`));
244
+ console.log(chalk.dim(` Install mode: ${installMode}\n`));
190
245
 
191
- const cmd = commands[pm] || commands.npm;
192
- console.log(chalk.green(` → ${cmd}\n`));
246
+ const commands = [getGlobalInstallCommand(pm, PKG_NAME, "update")];
247
+
248
+ if (installMode === "desktop") {
249
+ commands.push(getGlobalInstallCommand(pm, DESKTOP_PKG_NAME, "update"));
250
+ }
193
251
 
194
252
  try {
195
- execSync(cmd, { stdio: "inherit" });
196
- console.log(chalk.green("\n ThreatCrush updated successfully!\n"));
253
+ for (const cmd of commands) {
254
+ console.log(chalk.green(` ${cmd}\n`));
255
+ execSync(cmd, { stdio: "inherit" });
256
+ }
197
257
 
198
- // Show new version
199
- try {
200
- const newVersion = execSync(`${pm === "npm" ? "npm" : pm} list -g ${PKG_NAME} --depth=0`, {
201
- encoding: "utf-8",
202
- stdio: ["pipe", "pipe", "pipe"],
203
- });
204
- const match = newVersion.match(/@[\d.]+/);
205
- if (match) {
206
- console.log(chalk.dim(` Version: ${match[0]}\n`));
207
- }
208
- } catch {}
258
+ console.log(chalk.green("\n ✓ ThreatCrush updated successfully!\n"));
259
+ if (installMode === "desktop") {
260
+ console.log(chalk.dim(" Updated bundle: CLI + desktop app\n"));
261
+ } else {
262
+ console.log(chalk.dim(" Updated bundle: CLI only\n"));
263
+ }
209
264
  } catch (err) {
210
265
  console.log(chalk.red("\n ✗ Update failed. Try manually:\n"));
211
- console.log(chalk.dim(` ${cmd}\n`));
266
+ for (const cmd of commands) {
267
+ console.log(chalk.dim(` ${cmd}`));
268
+ }
269
+ console.log();
212
270
  }
213
271
  });
214
272
 
215
273
  program
216
274
  .command("remove")
217
- .description("Uninstall ThreatCrush CLI completely")
275
+ .description("Uninstall ThreatCrush and the installed bundle")
218
276
  .alias("uninstall")
219
277
  .action(async () => {
220
278
  console.log(LOGO);
@@ -233,28 +291,34 @@ program
233
291
  }
234
292
 
235
293
  const pm = detectPackageManager();
236
- console.log(chalk.dim(`\n Detected package manager: ${pm}\n`));
294
+ const installConfig = readInstallConfig();
295
+ const installMode = installConfig.installMode || "server";
296
+ const commands = [getGlobalInstallCommand(pm, PKG_NAME, "remove")];
237
297
 
238
- const commands: Record<string, string> = {
239
- npm: `npm uninstall -g ${PKG_NAME}`,
240
- pnpm: `pnpm remove -g ${PKG_NAME}`,
241
- yarn: `yarn global remove ${PKG_NAME}`,
242
- bun: `bun remove -g ${PKG_NAME}`,
243
- };
298
+ if (installMode === "desktop" && packageLooksInstalled(pm, DESKTOP_PKG_NAME)) {
299
+ commands.push(getGlobalInstallCommand(pm, DESKTOP_PKG_NAME, "remove"));
300
+ }
244
301
 
245
- const cmd = commands[pm] || commands.npm;
246
- console.log(chalk.green(` ${cmd}\n`));
302
+ console.log(chalk.dim(`\n Detected package manager: ${pm}`));
303
+ console.log(chalk.dim(` Install mode: ${installMode}\n`));
247
304
 
248
305
  try {
249
- execSync(cmd, { stdio: "inherit" });
306
+ for (const cmd of commands) {
307
+ console.log(chalk.green(` → ${cmd}\n`));
308
+ execSync(cmd, { stdio: "inherit" });
309
+ }
250
310
  console.log(chalk.green("\n ✓ ThreatCrush has been uninstalled.\n"));
251
311
  console.log(chalk.dim(" We're sorry to see you go! 👋\n"));
252
312
  console.log(chalk.dim(" Config files may remain at /etc/threatcrush/"));
253
313
  console.log(chalk.dim(" Logs may remain at /var/log/threatcrush/"));
254
- console.log(chalk.dim(" State may remain at /var/lib/threatcrush/\n"));
314
+ console.log(chalk.dim(" State may remain at /var/lib/threatcrush/"));
315
+ console.log(chalk.dim(` Local install metadata may remain at ${INSTALL_CONFIG_PATH}\n`));
255
316
  } catch (err) {
256
317
  console.log(chalk.red("\n ✗ Uninstall failed. Try manually:\n"));
257
- console.log(chalk.dim(` ${cmd}\n`));
318
+ for (const cmd of commands) {
319
+ console.log(chalk.dim(` ${cmd}`));
320
+ }
321
+ console.log();
258
322
  }
259
323
  });
260
324