@profullstack/threatcrush 0.1.11 → 0.1.12

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": "@profullstack/threatcrush",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
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
@@ -4,8 +4,9 @@ import { Command } from "commander";
4
4
  import chalk from "chalk";
5
5
  import readline from "readline";
6
6
  import { execSync } from "node:child_process";
7
- import { readFileSync } from "node:fs";
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
8
8
  import { join } from "node:path";
9
+ import { homedir } from "node:os";
9
10
 
10
11
  let PKG_VERSION = "0.1.8";
11
12
  try {
@@ -266,15 +267,140 @@ program
266
267
  await emailGate();
267
268
  });
268
269
 
269
- program
270
+ const storeCmd = program
270
271
  .command("store")
271
272
  .description("Browse the module marketplace")
272
- .argument("[action]", "search | info | publish")
273
- .argument("[query]", "search query or module name")
274
273
  .action(async () => {
275
274
  await emailGate();
276
275
  });
277
276
 
277
+ storeCmd
278
+ .command("search <query>")
279
+ .description("Search for modules in the store")
280
+ .action(async () => {
281
+ await emailGate();
282
+ });
283
+
284
+ storeCmd
285
+ .command("publish <url>")
286
+ .description("Publish a module from a git URL or web URL")
287
+ .action(async (url: string) => {
288
+ console.log(LOGO);
289
+
290
+ // 1. Get email
291
+ const configPath = join(homedir(), ".threatcrush", "config.json");
292
+ let email = "";
293
+ try {
294
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
295
+ email = config.email || "";
296
+ } catch {}
297
+
298
+ if (!email) {
299
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
300
+ email = await new Promise<string>((resolve) => {
301
+ rl.question(chalk.green(" Enter your email: "), (answer) => {
302
+ rl.close();
303
+ resolve(answer.trim());
304
+ });
305
+ });
306
+
307
+ if (!email || !email.includes("@")) {
308
+ console.log(chalk.red("\n Invalid email.\n"));
309
+ return;
310
+ }
311
+
312
+ // Save email
313
+ try {
314
+ const dir = join(homedir(), ".threatcrush");
315
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
316
+ writeFileSync(configPath, JSON.stringify({ email }, null, 2));
317
+ console.log(chalk.dim(` Saved email to ${configPath}`));
318
+ } catch {}
319
+ }
320
+
321
+ console.log(chalk.dim(`\n Fetching metadata from ${url}...\n`));
322
+
323
+ // 2. Fetch metadata
324
+ let meta: Record<string, unknown>;
325
+ try {
326
+ const res = await fetch(`${API_URL}/api/modules/fetch-meta`, {
327
+ method: "POST",
328
+ headers: { "Content-Type": "application/json" },
329
+ body: JSON.stringify({ url, git_url: url }),
330
+ });
331
+ if (!res.ok) {
332
+ const err = await res.json().catch(() => ({ error: res.statusText }));
333
+ console.log(chalk.red(` ✗ Failed to fetch metadata: ${(err as Record<string, string>).error}\n`));
334
+ return;
335
+ }
336
+ meta = await res.json() as Record<string, unknown>;
337
+ } catch (err) {
338
+ console.log(chalk.red(` ✗ Failed to fetch metadata: ${err instanceof Error ? err.message : err}\n`));
339
+ return;
340
+ }
341
+
342
+ // 3. Preview
343
+ console.log(chalk.green(" ── Module Preview ──\n"));
344
+ console.log(` ${chalk.bold("Name:")} ${meta.name || chalk.dim("(none)")}`);
345
+ console.log(` ${chalk.bold("Display:")} ${meta.display_name || chalk.dim("(none)")}`);
346
+ console.log(` ${chalk.bold("Description:")} ${meta.description || chalk.dim("(none)")}`);
347
+ console.log(` ${chalk.bold("Version:")} ${meta.version || "0.1.0"}`);
348
+ console.log(` ${chalk.bold("License:")} ${meta.license || "MIT"}`);
349
+ console.log(` ${chalk.bold("Author:")} ${meta.author_name || chalk.dim("(none)")}`);
350
+ console.log(` ${chalk.bold("Homepage:")} ${meta.homepage_url || chalk.dim("(none)")}`);
351
+ console.log(` ${chalk.bold("Git:")} ${meta.git_url || url}`);
352
+ if (Array.isArray(meta.tags) && meta.tags.length > 0) {
353
+ console.log(` ${chalk.bold("Tags:")} ${(meta.tags as string[]).join(", ")}`);
354
+ }
355
+ if (meta.stars) {
356
+ console.log(` ${chalk.bold("Stars:")} ⭐ ${meta.stars}`);
357
+ }
358
+ console.log();
359
+
360
+ // 4. Confirm
361
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
362
+ const confirm = await new Promise<string>((resolve) => {
363
+ rl.question(chalk.yellow(" Publish this module? (y/N): "), (answer) => {
364
+ rl.close();
365
+ resolve(answer.trim().toLowerCase());
366
+ });
367
+ });
368
+
369
+ if (confirm !== "y" && confirm !== "yes") {
370
+ console.log(chalk.dim("\n Cancelled.\n"));
371
+ return;
372
+ }
373
+
374
+ // 5. Publish
375
+ console.log(chalk.dim("\n Publishing..."));
376
+ try {
377
+ const res = await fetch(`${API_URL}/api/modules`, {
378
+ method: "POST",
379
+ headers: { "Content-Type": "application/json" },
380
+ body: JSON.stringify({
381
+ ...meta,
382
+ author_email: email,
383
+ git_url: (meta.git_url as string) || url,
384
+ homepage_url: (meta.homepage_url as string) || url,
385
+ }),
386
+ });
387
+
388
+ const result = await res.json() as Record<string, unknown>;
389
+
390
+ if (!res.ok) {
391
+ console.log(chalk.red(`\n ✗ Publish failed: ${(result as Record<string, string>).error}\n`));
392
+ return;
393
+ }
394
+
395
+ const mod = result.module as Record<string, string>;
396
+ const slug = mod?.slug || meta.name;
397
+ console.log(chalk.green(`\n ✓ Module published!`));
398
+ console.log(chalk.dim(` ${API_URL}/store/${slug}\n`));
399
+ } catch (err) {
400
+ console.log(chalk.red(`\n ✗ Publish failed: ${err instanceof Error ? err.message : err}\n`));
401
+ }
402
+ });
403
+
278
404
  // Default action (no command — show help)
279
405
  program.action(() => {
280
406
  console.log(LOGO);