@profullstack/threatcrush 0.1.16 → 0.2.1

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/src/core/state.ts DELETED
@@ -1,146 +0,0 @@
1
- import Database from 'better-sqlite3';
2
- import type { ThreatEvent } from '../types/events.js';
3
-
4
- let db: Database.Database | null = null;
5
-
6
- export function initStateDB(dbPath: string = '/var/lib/threatcrush/state.db'): Database.Database {
7
- if (db) return db;
8
-
9
- try {
10
- db = new Database(dbPath);
11
- } catch {
12
- // Fall back to in-memory if we can't write to the path
13
- db = new Database(':memory:');
14
- }
15
-
16
- db.pragma('journal_mode = WAL');
17
-
18
- db.exec(`
19
- CREATE TABLE IF NOT EXISTS events (
20
- id INTEGER PRIMARY KEY AUTOINCREMENT,
21
- timestamp TEXT NOT NULL,
22
- module TEXT NOT NULL,
23
- category TEXT NOT NULL,
24
- severity TEXT NOT NULL,
25
- message TEXT NOT NULL,
26
- source_ip TEXT,
27
- details TEXT
28
- );
29
-
30
- CREATE TABLE IF NOT EXISTS module_state (
31
- module TEXT NOT NULL,
32
- key TEXT NOT NULL,
33
- value TEXT,
34
- PRIMARY KEY (module, key)
35
- );
36
-
37
- CREATE TABLE IF NOT EXISTS stats (
38
- key TEXT PRIMARY KEY,
39
- value TEXT NOT NULL,
40
- updated_at TEXT NOT NULL
41
- );
42
-
43
- CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
44
- CREATE INDEX IF NOT EXISTS idx_events_module ON events(module);
45
- CREATE INDEX IF NOT EXISTS idx_events_severity ON events(severity);
46
- CREATE INDEX IF NOT EXISTS idx_events_source_ip ON events(source_ip);
47
- `);
48
-
49
- return db;
50
- }
51
-
52
- export function insertEvent(event: ThreatEvent): number {
53
- const database = db || initStateDB();
54
- const stmt = database.prepare(`
55
- INSERT INTO events (timestamp, module, category, severity, message, source_ip, details)
56
- VALUES (?, ?, ?, ?, ?, ?, ?)
57
- `);
58
- const result = stmt.run(
59
- event.timestamp.toISOString(),
60
- event.module,
61
- event.category,
62
- event.severity,
63
- event.message,
64
- event.source_ip || null,
65
- event.details ? JSON.stringify(event.details) : null,
66
- );
67
- return result.lastInsertRowid as number;
68
- }
69
-
70
- export function getRecentEvents(limit: number = 50): ThreatEvent[] {
71
- const database = db || initStateDB();
72
- const rows = database.prepare(`
73
- SELECT * FROM events ORDER BY timestamp DESC LIMIT ?
74
- `).all(limit) as any[];
75
- return rows.map(rowToEvent);
76
- }
77
-
78
- export function getEventCount(since?: Date): number {
79
- const database = db || initStateDB();
80
- if (since) {
81
- return (database.prepare(`SELECT COUNT(*) as count FROM events WHERE timestamp >= ?`)
82
- .get(since.toISOString()) as any).count;
83
- }
84
- return (database.prepare(`SELECT COUNT(*) as count FROM events`).get() as any).count;
85
- }
86
-
87
- export function getThreatCount(since?: Date): number {
88
- const database = db || initStateDB();
89
- const severities = "('medium','high','critical')";
90
- if (since) {
91
- return (database.prepare(
92
- `SELECT COUNT(*) as count FROM events WHERE severity IN ${severities} AND timestamp >= ?`
93
- ).get(since.toISOString()) as any).count;
94
- }
95
- return (database.prepare(
96
- `SELECT COUNT(*) as count FROM events WHERE severity IN ${severities}`
97
- ).get() as any).count;
98
- }
99
-
100
- export function getTopSources(limit: number = 10): Array<{ ip: string; count: number }> {
101
- const database = db || initStateDB();
102
- return database.prepare(`
103
- SELECT source_ip as ip, COUNT(*) as count FROM events
104
- WHERE source_ip IS NOT NULL
105
- GROUP BY source_ip ORDER BY count DESC LIMIT ?
106
- `).all(limit) as any[];
107
- }
108
-
109
- export function getModuleState(module: string, key: string): unknown {
110
- const database = db || initStateDB();
111
- const row = database.prepare(`SELECT value FROM module_state WHERE module = ? AND key = ?`)
112
- .get(module, key) as any;
113
- if (!row) return undefined;
114
- try {
115
- return JSON.parse(row.value);
116
- } catch {
117
- return row.value;
118
- }
119
- }
120
-
121
- export function setModuleState(module: string, key: string, value: unknown): void {
122
- const database = db || initStateDB();
123
- database.prepare(`
124
- INSERT OR REPLACE INTO module_state (module, key, value) VALUES (?, ?, ?)
125
- `).run(module, key, JSON.stringify(value));
126
- }
127
-
128
- function rowToEvent(row: any): ThreatEvent {
129
- return {
130
- id: row.id,
131
- timestamp: new Date(row.timestamp),
132
- module: row.module,
133
- category: row.category,
134
- severity: row.severity,
135
- message: row.message,
136
- source_ip: row.source_ip,
137
- details: row.details ? JSON.parse(row.details) : undefined,
138
- };
139
- }
140
-
141
- export function closeDB(): void {
142
- if (db) {
143
- db.close();
144
- db = null;
145
- }
146
- }
package/src/index.ts DELETED
@@ -1,474 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from "commander";
4
- import chalk from "chalk";
5
- import readline from "readline";
6
- import { execSync } from "node:child_process";
7
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
8
- import { join } from "node:path";
9
- import { homedir } from "node:os";
10
-
11
- let PKG_VERSION = "0.1.8";
12
- try {
13
- const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
14
- PKG_VERSION = pkg.version;
15
- } catch {}
16
-
17
- const LOGO = `
18
- ${chalk.green(" ████████╗██╗ ██╗██████╗ ███████╗ █████╗ ████████╗")}
19
- ${chalk.green(" ╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔══██╗╚══██╔══╝")}
20
- ${chalk.green(" ██║ ███████║██████╔╝█████╗ ███████║ ██║ ")}
21
- ${chalk.green(" ██║ ██╔══██║██╔══██╗██╔══╝ ██╔══██║ ██║ ")}
22
- ${chalk.green(" ██║ ██║ ██║██║ ██║███████╗██║ ██║ ██║ ")}
23
- ${chalk.green(" ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝")}
24
- ${chalk.dim(" C R U S H")}
25
- `;
26
-
27
- const API_URL = process.env.THREATCRUSH_API_URL || "https://threatcrush.com";
28
- const PKG_NAME = "@profullstack/threatcrush";
29
- const DESKTOP_PKG_NAME = "@profullstack/threatcrush-desktop";
30
- const INSTALL_CONFIG_PATH = join(homedir(), ".threatcrush", "install.json");
31
-
32
- // ─── Helpers ───
33
-
34
- async function promptEmail(): Promise<string | null> {
35
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
36
- return new Promise((resolve) => {
37
- rl.question(chalk.green("\n Enter your email to continue: "), (answer) => {
38
- rl.close();
39
- resolve(answer.trim() || null);
40
- });
41
- });
42
- }
43
-
44
- async function joinWaitlist(email: string): Promise<{ referral_code?: string } | null> {
45
- try {
46
- const res = await fetch(`${API_URL}/api/waitlist`, {
47
- method: "POST",
48
- headers: { "Content-Type": "application/json" },
49
- body: JSON.stringify({ email }),
50
- });
51
- return await res.json();
52
- } catch {
53
- return null;
54
- }
55
- }
56
-
57
- async function emailGate(): Promise<boolean> {
58
- console.log(LOGO);
59
- console.log(chalk.yellow(" ⚡ Coming soon — ThreatCrush is in private beta.\n"));
60
-
61
- const email = await promptEmail();
62
- if (!email || !email.includes("@")) {
63
- console.log(chalk.red("\n Invalid email. Try again.\n"));
64
- return false;
65
- }
66
-
67
- const result = await joinWaitlist(email);
68
- if (result?.referral_code) {
69
- console.log(chalk.green(`\n ✓ You're on the list!`));
70
- console.log(chalk.dim(` Referral code: ${chalk.white(result.referral_code)}`));
71
- console.log(chalk.dim(` Share: ${API_URL}?ref=${result.referral_code}`));
72
- console.log(chalk.green(`\n 🎁 Refer a friend → they save $100, you earn $100 in crypto via CoinPayPortal\n`));
73
- } else {
74
- console.log(chalk.green(`\n ✓ Thanks! We'll notify you when ThreatCrush launches.\n`));
75
- }
76
-
77
- return true;
78
- }
79
-
80
- function detectPackageManager(): string {
81
- // Check what installed us
82
- try {
83
- const npmGlobal = execSync("npm ls -g --depth=0 --json 2>/dev/null", { encoding: "utf-8" });
84
- if (npmGlobal.includes(PKG_NAME)) return "npm";
85
- } catch {}
86
- try {
87
- execSync("pnpm --version", { stdio: "pipe" });
88
- return "pnpm";
89
- } catch {}
90
- try {
91
- execSync("yarn --version", { stdio: "pipe" });
92
- return "yarn";
93
- } catch {}
94
- try {
95
- execSync("bun --version", { stdio: "pipe" });
96
- return "bun";
97
- } catch {}
98
- return "npm";
99
- }
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
-
156
- // ─── Program ───
157
-
158
- const program = new Command();
159
-
160
- program
161
- .name("threatcrush")
162
- .description(
163
- `${chalk.green("⚡ ThreatCrush")} — All-in-one security agent
164
-
165
- Monitor every connection on every port. Detect live attacks,
166
- scan your code, pentest your APIs, and alert you in real-time.
167
-
168
- ${chalk.dim("Website:")} ${chalk.green("https://threatcrush.com")}
169
- ${chalk.dim("GitHub:")} ${chalk.green("https://github.com/profullstack/threatcrush")}
170
- ${chalk.dim("npm:")} ${chalk.green("https://www.npmjs.com/package/@profullstack/threatcrush")}
171
- ${chalk.dim("License:")} ${chalk.green("$499 lifetime")} (or $399 with referral)
172
-
173
- ${chalk.dim("Examples:")}
174
- ${chalk.green("$")} threatcrush monitor ${chalk.dim("# Real-time monitoring")}
175
- ${chalk.green("$")} threatcrush tui ${chalk.dim("# Interactive dashboard")}
176
- ${chalk.green("$")} threatcrush scan ./src ${chalk.dim("# Scan code for vulns")}
177
- ${chalk.green("$")} threatcrush pentest URL ${chalk.dim("# Pen test a URL")}
178
- ${chalk.green("$")} threatcrush modules install ${chalk.dim("# Install a module")}
179
- ${chalk.green("$")} threatcrush update ${chalk.dim("# Update to latest")}
180
- ${chalk.green("$")} threatcrush remove ${chalk.dim("# Uninstall completely")}`)
181
- .version(PKG_VERSION, "-v, --version", "Show version number")
182
- .helpOption("-h, --help", "Show this help")
183
- .addHelpText("after", `
184
- ${chalk.dim("─────────────────────────────────────────────────────")}
185
- ${chalk.dim("Modules:")}
186
- ThreatCrush uses pluggable security modules. Core modules included:
187
- ${chalk.green("network-monitor")} All TCP/UDP traffic, port scans, SYN floods
188
- ${chalk.green("log-watcher")} nginx, Apache, syslog, journald
189
- ${chalk.green("ssh-guard")} Failed logins, brute force, tunneling
190
- ${chalk.green("code-scanner")} Vulnerabilities, secrets, dependency CVEs
191
- ${chalk.green("pentest-engine")} SQLi, XSS, SSRF, API fuzzing
192
- ${chalk.green("dns-monitor")} DNS tunneling, DGA detection
193
- ${chalk.green("firewall-rules")} Auto-blocks via iptables/nftables
194
- ${chalk.green("alert-system")} Slack, Discord, email, webhook, PagerDuty
195
-
196
- Browse community modules: ${chalk.green("threatcrush store")}
197
- `);
198
-
199
- // ─── Gated commands (coming soon) ───
200
-
201
- const gatedCommand = (name: string, desc: string, aliases?: string[]) => {
202
- const cmd = program.command(name).description(desc).action(async () => {
203
- await emailGate();
204
- });
205
- if (aliases) {
206
- for (const alias of aliases) {
207
- cmd.alias(alias);
208
- }
209
- }
210
- };
211
-
212
- gatedCommand("monitor", "Real-time security monitoring (all ports, all protocols)");
213
- gatedCommand("tui", "Interactive security dashboard (htop for security)", ["dashboard"]);
214
- gatedCommand("init", "Auto-detect services and configure ThreatCrush");
215
- gatedCommand("scan", "Scan codebase for vulnerabilities and secrets");
216
- gatedCommand("pentest", "Penetration test URLs and APIs");
217
- gatedCommand("status", "Show daemon status and loaded modules");
218
- gatedCommand("start", "Start the ThreatCrush daemon");
219
- gatedCommand("stop", "Stop the ThreatCrush daemon");
220
- gatedCommand("logs", "Tail daemon logs");
221
- gatedCommand("activate", "Activate your license key");
222
-
223
- // ─── Real commands ───
224
-
225
- program
226
- .command("update")
227
- .description("Update ThreatCrush CLI and installed bundle")
228
- .option("--cli", "Update CLI only")
229
- .option("--modules", "Update modules only")
230
- .option("--desktop", "Update desktop app too")
231
- .action(async (opts) => {
232
- console.log(LOGO);
233
-
234
- if (opts.modules) {
235
- console.log(chalk.yellow(" Module updates coming soon.\n"));
236
- return;
237
- }
238
-
239
- const pm = detectPackageManager();
240
- const installConfig = readInstallConfig();
241
- const installMode = opts.cli ? "server" : (opts.desktop ? "desktop" : installConfig.installMode || "server");
242
-
243
- console.log(chalk.dim(` Detected package manager: ${pm}`));
244
- console.log(chalk.dim(` Install mode: ${installMode}\n`));
245
-
246
- const commands = [getGlobalInstallCommand(pm, PKG_NAME, "update")];
247
-
248
- if (installMode === "desktop") {
249
- commands.push(getGlobalInstallCommand(pm, DESKTOP_PKG_NAME, "update"));
250
- }
251
-
252
- try {
253
- for (const cmd of commands) {
254
- console.log(chalk.green(` → ${cmd}\n`));
255
- execSync(cmd, { stdio: "inherit" });
256
- }
257
-
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
- }
264
- } catch (err) {
265
- console.log(chalk.red("\n ✗ Update failed. Try manually:\n"));
266
- for (const cmd of commands) {
267
- console.log(chalk.dim(` ${cmd}`));
268
- }
269
- console.log();
270
- }
271
- });
272
-
273
- program
274
- .command("remove")
275
- .description("Uninstall ThreatCrush and the installed bundle")
276
- .alias("uninstall")
277
- .action(async () => {
278
- console.log(LOGO);
279
-
280
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
281
- const confirm = await new Promise<string>((resolve) => {
282
- rl.question(chalk.yellow(" Are you sure you want to uninstall ThreatCrush? (y/N): "), (answer) => {
283
- rl.close();
284
- resolve(answer.trim().toLowerCase());
285
- });
286
- });
287
-
288
- if (confirm !== "y" && confirm !== "yes") {
289
- console.log(chalk.dim("\n Cancelled.\n"));
290
- return;
291
- }
292
-
293
- const pm = detectPackageManager();
294
- const installConfig = readInstallConfig();
295
- const installMode = installConfig.installMode || "server";
296
- const commands = [getGlobalInstallCommand(pm, PKG_NAME, "remove")];
297
-
298
- if (installMode === "desktop" && packageLooksInstalled(pm, DESKTOP_PKG_NAME)) {
299
- commands.push(getGlobalInstallCommand(pm, DESKTOP_PKG_NAME, "remove"));
300
- }
301
-
302
- console.log(chalk.dim(`\n Detected package manager: ${pm}`));
303
- console.log(chalk.dim(` Install mode: ${installMode}\n`));
304
-
305
- try {
306
- for (const cmd of commands) {
307
- console.log(chalk.green(` → ${cmd}\n`));
308
- execSync(cmd, { stdio: "inherit" });
309
- }
310
- console.log(chalk.green("\n ✓ ThreatCrush has been uninstalled.\n"));
311
- console.log(chalk.dim(" We're sorry to see you go! 👋\n"));
312
- console.log(chalk.dim(" Config files may remain at /etc/threatcrush/"));
313
- console.log(chalk.dim(" Logs may remain at /var/log/threatcrush/"));
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`));
316
- } catch (err) {
317
- console.log(chalk.red("\n ✗ Uninstall failed. Try manually:\n"));
318
- for (const cmd of commands) {
319
- console.log(chalk.dim(` ${cmd}`));
320
- }
321
- console.log();
322
- }
323
- });
324
-
325
- program
326
- .command("modules")
327
- .description("Manage security modules")
328
- .argument("[action]", "list | install | remove | available | update")
329
- .argument("[name]", "module name")
330
- .action(async () => {
331
- await emailGate();
332
- });
333
-
334
- const storeCmd = program
335
- .command("store")
336
- .description("Browse the module marketplace")
337
- .action(async () => {
338
- await emailGate();
339
- });
340
-
341
- storeCmd
342
- .command("search <query>")
343
- .description("Search for modules in the store")
344
- .action(async () => {
345
- await emailGate();
346
- });
347
-
348
- storeCmd
349
- .command("publish <url>")
350
- .description("Publish a module from a git URL or web URL")
351
- .action(async (url: string) => {
352
- console.log(LOGO);
353
-
354
- // 1. Get email
355
- const configPath = join(homedir(), ".threatcrush", "config.json");
356
- let email = "";
357
- try {
358
- const config = JSON.parse(readFileSync(configPath, "utf-8"));
359
- email = config.email || "";
360
- } catch {}
361
-
362
- if (!email) {
363
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
364
- email = await new Promise<string>((resolve) => {
365
- rl.question(chalk.green(" Enter your email: "), (answer) => {
366
- rl.close();
367
- resolve(answer.trim());
368
- });
369
- });
370
-
371
- if (!email || !email.includes("@")) {
372
- console.log(chalk.red("\n Invalid email.\n"));
373
- return;
374
- }
375
-
376
- // Save email
377
- try {
378
- const dir = join(homedir(), ".threatcrush");
379
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
380
- writeFileSync(configPath, JSON.stringify({ email }, null, 2));
381
- console.log(chalk.dim(` Saved email to ${configPath}`));
382
- } catch {}
383
- }
384
-
385
- console.log(chalk.dim(`\n Fetching metadata from ${url}...\n`));
386
-
387
- // 2. Fetch metadata
388
- let meta: Record<string, unknown>;
389
- try {
390
- const res = await fetch(`${API_URL}/api/modules/fetch-meta`, {
391
- method: "POST",
392
- headers: { "Content-Type": "application/json" },
393
- body: JSON.stringify({ url, git_url: url }),
394
- });
395
- if (!res.ok) {
396
- const err = await res.json().catch(() => ({ error: res.statusText }));
397
- console.log(chalk.red(` ✗ Failed to fetch metadata: ${(err as Record<string, string>).error}\n`));
398
- return;
399
- }
400
- meta = await res.json() as Record<string, unknown>;
401
- } catch (err) {
402
- console.log(chalk.red(` ✗ Failed to fetch metadata: ${err instanceof Error ? err.message : err}\n`));
403
- return;
404
- }
405
-
406
- // 3. Preview
407
- console.log(chalk.green(" ── Module Preview ──\n"));
408
- console.log(` ${chalk.bold("Name:")} ${meta.name || chalk.dim("(none)")}`);
409
- console.log(` ${chalk.bold("Display:")} ${meta.display_name || chalk.dim("(none)")}`);
410
- console.log(` ${chalk.bold("Description:")} ${meta.description || chalk.dim("(none)")}`);
411
- console.log(` ${chalk.bold("Version:")} ${meta.version || "0.1.0"}`);
412
- console.log(` ${chalk.bold("License:")} ${meta.license || "MIT"}`);
413
- console.log(` ${chalk.bold("Author:")} ${meta.author_name || chalk.dim("(none)")}`);
414
- console.log(` ${chalk.bold("Homepage:")} ${meta.homepage_url || chalk.dim("(none)")}`);
415
- console.log(` ${chalk.bold("Git:")} ${meta.git_url || url}`);
416
- if (Array.isArray(meta.tags) && meta.tags.length > 0) {
417
- console.log(` ${chalk.bold("Tags:")} ${(meta.tags as string[]).join(", ")}`);
418
- }
419
- if (meta.stars) {
420
- console.log(` ${chalk.bold("Stars:")} ⭐ ${meta.stars}`);
421
- }
422
- console.log();
423
-
424
- // 4. Confirm
425
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
426
- const confirm = await new Promise<string>((resolve) => {
427
- rl.question(chalk.yellow(" Publish this module? (y/N): "), (answer) => {
428
- rl.close();
429
- resolve(answer.trim().toLowerCase());
430
- });
431
- });
432
-
433
- if (confirm !== "y" && confirm !== "yes") {
434
- console.log(chalk.dim("\n Cancelled.\n"));
435
- return;
436
- }
437
-
438
- // 5. Publish
439
- console.log(chalk.dim("\n Publishing..."));
440
- try {
441
- const res = await fetch(`${API_URL}/api/modules`, {
442
- method: "POST",
443
- headers: { "Content-Type": "application/json" },
444
- body: JSON.stringify({
445
- ...meta,
446
- author_email: email,
447
- git_url: (meta.git_url as string) || url,
448
- homepage_url: (meta.homepage_url as string) || url,
449
- }),
450
- });
451
-
452
- const result = await res.json() as Record<string, unknown>;
453
-
454
- if (!res.ok) {
455
- console.log(chalk.red(`\n ✗ Publish failed: ${(result as Record<string, string>).error}\n`));
456
- return;
457
- }
458
-
459
- const mod = result.module as Record<string, string>;
460
- const slug = mod?.slug || meta.name;
461
- console.log(chalk.green(`\n ✓ Module published!`));
462
- console.log(chalk.dim(` ${API_URL}/store/${slug}\n`));
463
- } catch (err) {
464
- console.log(chalk.red(`\n ✗ Publish failed: ${err instanceof Error ? err.message : err}\n`));
465
- }
466
- });
467
-
468
- // Default action (no command — show help)
469
- program.action(() => {
470
- console.log(LOGO);
471
- program.help();
472
- });
473
-
474
- program.parse();
@@ -1,63 +0,0 @@
1
- export interface DaemonConfig {
2
- pid_file: string;
3
- log_level: 'debug' | 'info' | 'warn' | 'error';
4
- log_file: string;
5
- state_db: string;
6
- }
7
-
8
- export interface ApiConfig {
9
- enabled: boolean;
10
- bind: string;
11
- tls: boolean;
12
- }
13
-
14
- export interface AlertChannelConfig {
15
- enabled: boolean;
16
- [key: string]: unknown;
17
- }
18
-
19
- export interface ModulesConfig {
20
- auto_update: boolean;
21
- update_interval: string;
22
- module_dir: string;
23
- config_dir: string;
24
- }
25
-
26
- export interface ThreatCrushConfig {
27
- daemon: DaemonConfig;
28
- api: ApiConfig;
29
- alerts: Record<string, AlertChannelConfig>;
30
- modules: ModulesConfig;
31
- license?: {
32
- key_file?: string;
33
- key?: string;
34
- };
35
- }
36
-
37
- export interface ModuleManifest {
38
- module: {
39
- name: string;
40
- version: string;
41
- description: string;
42
- author: string;
43
- license: string;
44
- homepage?: string;
45
- pricing?: {
46
- type: 'free' | 'paid' | 'freemium';
47
- price_usd?: number;
48
- };
49
- requirements?: {
50
- threatcrush?: string;
51
- os?: string[];
52
- capabilities?: string[];
53
- };
54
- config?: {
55
- defaults?: Record<string, unknown>;
56
- };
57
- };
58
- }
59
-
60
- export interface ModuleConfig {
61
- enabled: boolean;
62
- [key: string]: unknown;
63
- }