@profullstack/threatcrush 0.1.10 → 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/README.md +10 -0
- package/dist/index.js +114 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +130 -4
package/package.json
CHANGED
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);
|