@hasna/microservices 0.0.4 → 0.0.5
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/microservices/microservice-ads/src/cli/index.ts +198 -0
- package/microservices/microservice-ads/src/db/campaigns.ts +304 -0
- package/microservices/microservice-ads/src/mcp/index.ts +160 -0
- package/microservices/microservice-contracts/src/cli/index.ts +410 -23
- package/microservices/microservice-contracts/src/db/contracts.ts +430 -1
- package/microservices/microservice-contracts/src/db/migrations.ts +83 -0
- package/microservices/microservice-contracts/src/mcp/index.ts +312 -3
- package/microservices/microservice-domains/src/cli/index.ts +253 -0
- package/microservices/microservice-domains/src/db/domains.ts +613 -0
- package/microservices/microservice-domains/src/index.ts +21 -0
- package/microservices/microservice-domains/src/mcp/index.ts +168 -0
- package/microservices/microservice-hiring/src/cli/index.ts +318 -8
- package/microservices/microservice-hiring/src/db/hiring.ts +503 -0
- package/microservices/microservice-hiring/src/db/migrations.ts +21 -0
- package/microservices/microservice-hiring/src/index.ts +29 -0
- package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
- package/microservices/microservice-hiring/src/mcp/index.ts +245 -0
- package/microservices/microservice-payments/src/cli/index.ts +255 -3
- package/microservices/microservice-payments/src/db/migrations.ts +18 -0
- package/microservices/microservice-payments/src/db/payments.ts +552 -0
- package/microservices/microservice-payments/src/mcp/index.ts +223 -0
- package/microservices/microservice-payroll/src/cli/index.ts +269 -0
- package/microservices/microservice-payroll/src/db/migrations.ts +26 -0
- package/microservices/microservice-payroll/src/db/payroll.ts +636 -0
- package/microservices/microservice-payroll/src/mcp/index.ts +246 -0
- package/microservices/microservice-shipping/src/cli/index.ts +211 -3
- package/microservices/microservice-shipping/src/db/migrations.ts +8 -0
- package/microservices/microservice-shipping/src/db/shipping.ts +453 -3
- package/microservices/microservice-shipping/src/mcp/index.ts +149 -1
- package/microservices/microservice-social/src/cli/index.ts +244 -2
- package/microservices/microservice-social/src/db/migrations.ts +33 -0
- package/microservices/microservice-social/src/db/social.ts +378 -4
- package/microservices/microservice-social/src/mcp/index.ts +221 -1
- package/microservices/microservice-subscriptions/src/cli/index.ts +315 -0
- package/microservices/microservice-subscriptions/src/db/migrations.ts +68 -0
- package/microservices/microservice-subscriptions/src/db/subscriptions.ts +567 -3
- package/microservices/microservice-subscriptions/src/mcp/index.ts +267 -1
- package/package.json +1 -1
|
@@ -13,16 +13,33 @@ import {
|
|
|
13
13
|
listExpiring,
|
|
14
14
|
renewContract,
|
|
15
15
|
getContractStats,
|
|
16
|
+
submitForReview,
|
|
17
|
+
approveContract,
|
|
18
|
+
getContractHistory,
|
|
19
|
+
recordSignature,
|
|
20
|
+
listSignatures,
|
|
21
|
+
compareContracts,
|
|
22
|
+
exportContract,
|
|
16
23
|
} from "../db/contracts.js";
|
|
17
24
|
import {
|
|
18
25
|
createClause,
|
|
19
26
|
listClauses,
|
|
20
27
|
deleteClause,
|
|
28
|
+
addClauseFromTemplate,
|
|
29
|
+
saveClauseTemplate,
|
|
30
|
+
listClauseTemplates,
|
|
21
31
|
} from "../db/contracts.js";
|
|
22
32
|
import {
|
|
23
33
|
createReminder,
|
|
24
34
|
listReminders,
|
|
25
35
|
deleteReminder,
|
|
36
|
+
setMultiReminders,
|
|
37
|
+
} from "../db/contracts.js";
|
|
38
|
+
import {
|
|
39
|
+
createObligation,
|
|
40
|
+
listObligations,
|
|
41
|
+
completeObligation,
|
|
42
|
+
listOverdueObligations,
|
|
26
43
|
} from "../db/contracts.js";
|
|
27
44
|
|
|
28
45
|
const server = new McpServer({
|
|
@@ -40,7 +57,7 @@ server.registerTool(
|
|
|
40
57
|
inputSchema: {
|
|
41
58
|
title: z.string(),
|
|
42
59
|
type: z.enum(["nda", "service", "employment", "license", "other"]).optional(),
|
|
43
|
-
status: z.enum(["draft", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
60
|
+
status: z.enum(["draft", "pending_review", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
44
61
|
counterparty: z.string().optional(),
|
|
45
62
|
counterparty_email: z.string().optional(),
|
|
46
63
|
start_date: z.string().optional(),
|
|
@@ -82,7 +99,7 @@ server.registerTool(
|
|
|
82
99
|
inputSchema: {
|
|
83
100
|
search: z.string().optional(),
|
|
84
101
|
type: z.enum(["nda", "service", "employment", "license", "other"]).optional(),
|
|
85
|
-
status: z.enum(["draft", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
102
|
+
status: z.enum(["draft", "pending_review", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
86
103
|
counterparty: z.string().optional(),
|
|
87
104
|
limit: z.number().optional(),
|
|
88
105
|
},
|
|
@@ -109,7 +126,7 @@ server.registerTool(
|
|
|
109
126
|
id: z.string(),
|
|
110
127
|
title: z.string().optional(),
|
|
111
128
|
type: z.enum(["nda", "service", "employment", "license", "other"]).optional(),
|
|
112
|
-
status: z.enum(["draft", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
129
|
+
status: z.enum(["draft", "pending_review", "pending_signature", "active", "expired", "terminated"]).optional(),
|
|
113
130
|
counterparty: z.string().optional(),
|
|
114
131
|
counterparty_email: z.string().optional(),
|
|
115
132
|
start_date: z.string().optional(),
|
|
@@ -209,6 +226,148 @@ server.registerTool(
|
|
|
209
226
|
}
|
|
210
227
|
);
|
|
211
228
|
|
|
229
|
+
// --- Approval workflow ---
|
|
230
|
+
|
|
231
|
+
server.registerTool(
|
|
232
|
+
"submit_for_review",
|
|
233
|
+
{
|
|
234
|
+
title: "Submit for Review",
|
|
235
|
+
description: "Submit a draft contract for review (draft -> pending_review).",
|
|
236
|
+
inputSchema: { id: z.string() },
|
|
237
|
+
},
|
|
238
|
+
async ({ id }) => {
|
|
239
|
+
try {
|
|
240
|
+
const contract = submitForReview(id);
|
|
241
|
+
if (!contract) {
|
|
242
|
+
return { content: [{ type: "text", text: `Contract '${id}' not found.` }], isError: true };
|
|
243
|
+
}
|
|
244
|
+
return { content: [{ type: "text", text: JSON.stringify(contract, null, 2) }] };
|
|
245
|
+
} catch (err) {
|
|
246
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
server.registerTool(
|
|
252
|
+
"approve_contract",
|
|
253
|
+
{
|
|
254
|
+
title: "Approve Contract",
|
|
255
|
+
description: "Approve a contract, advancing it through the approval workflow (pending_review -> pending_signature -> active).",
|
|
256
|
+
inputSchema: { id: z.string() },
|
|
257
|
+
},
|
|
258
|
+
async ({ id }) => {
|
|
259
|
+
try {
|
|
260
|
+
const contract = approveContract(id);
|
|
261
|
+
if (!contract) {
|
|
262
|
+
return { content: [{ type: "text", text: `Contract '${id}' not found.` }], isError: true };
|
|
263
|
+
}
|
|
264
|
+
return { content: [{ type: "text", text: JSON.stringify(contract, null, 2) }] };
|
|
265
|
+
} catch (err) {
|
|
266
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// --- Version history ---
|
|
272
|
+
|
|
273
|
+
server.registerTool(
|
|
274
|
+
"contract_history",
|
|
275
|
+
{
|
|
276
|
+
title: "Contract History",
|
|
277
|
+
description: "Get version history for a contract, showing previous states before each update.",
|
|
278
|
+
inputSchema: { contract_id: z.string() },
|
|
279
|
+
},
|
|
280
|
+
async ({ contract_id }) => {
|
|
281
|
+
const history = getContractHistory(contract_id);
|
|
282
|
+
return {
|
|
283
|
+
content: [
|
|
284
|
+
{ type: "text", text: JSON.stringify({ history, count: history.length }, null, 2) },
|
|
285
|
+
],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// --- Signature logging ---
|
|
291
|
+
|
|
292
|
+
server.registerTool(
|
|
293
|
+
"record_signature",
|
|
294
|
+
{
|
|
295
|
+
title: "Record Signature",
|
|
296
|
+
description: "Record a signature on a contract.",
|
|
297
|
+
inputSchema: {
|
|
298
|
+
contract_id: z.string(),
|
|
299
|
+
signer_name: z.string(),
|
|
300
|
+
signer_email: z.string().optional(),
|
|
301
|
+
method: z.enum(["digital", "wet", "docusign"]).optional(),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
async (params) => {
|
|
305
|
+
const sig = recordSignature(params);
|
|
306
|
+
return { content: [{ type: "text", text: JSON.stringify(sig, null, 2) }] };
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
server.registerTool(
|
|
311
|
+
"list_signatures",
|
|
312
|
+
{
|
|
313
|
+
title: "List Signatures",
|
|
314
|
+
description: "List all signatures for a contract.",
|
|
315
|
+
inputSchema: { contract_id: z.string() },
|
|
316
|
+
},
|
|
317
|
+
async ({ contract_id }) => {
|
|
318
|
+
const sigs = listSignatures(contract_id);
|
|
319
|
+
return {
|
|
320
|
+
content: [
|
|
321
|
+
{ type: "text", text: JSON.stringify({ signatures: sigs, count: sigs.length }, null, 2) },
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// --- Contract comparison ---
|
|
328
|
+
|
|
329
|
+
server.registerTool(
|
|
330
|
+
"compare_contracts",
|
|
331
|
+
{
|
|
332
|
+
title: "Compare Contracts",
|
|
333
|
+
description: "Compare two contracts, showing field and clause differences.",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
id1: z.string(),
|
|
336
|
+
id2: z.string(),
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
async ({ id1, id2 }) => {
|
|
340
|
+
try {
|
|
341
|
+
const diff = compareContracts(id1, id2);
|
|
342
|
+
return { content: [{ type: "text", text: JSON.stringify(diff, null, 2) }] };
|
|
343
|
+
} catch (err) {
|
|
344
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// --- Markdown export ---
|
|
350
|
+
|
|
351
|
+
server.registerTool(
|
|
352
|
+
"export_contract",
|
|
353
|
+
{
|
|
354
|
+
title: "Export Contract",
|
|
355
|
+
description: "Export a contract as formatted markdown or JSON.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
id: z.string(),
|
|
358
|
+
format: z.enum(["md", "json"]).optional(),
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
async ({ id, format }) => {
|
|
362
|
+
try {
|
|
363
|
+
const output = exportContract(id, format || "md");
|
|
364
|
+
return { content: [{ type: "text", text: output }] };
|
|
365
|
+
} catch (err) {
|
|
366
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
|
|
212
371
|
// --- Clauses ---
|
|
213
372
|
|
|
214
373
|
server.registerTool(
|
|
@@ -259,6 +418,132 @@ server.registerTool(
|
|
|
259
418
|
}
|
|
260
419
|
);
|
|
261
420
|
|
|
421
|
+
// --- Clause templates ---
|
|
422
|
+
|
|
423
|
+
server.registerTool(
|
|
424
|
+
"save_clause_template",
|
|
425
|
+
{
|
|
426
|
+
title: "Save Clause Template",
|
|
427
|
+
description: "Save a clause as a reusable template in the clause library.",
|
|
428
|
+
inputSchema: {
|
|
429
|
+
name: z.string(),
|
|
430
|
+
text: z.string(),
|
|
431
|
+
type: z.enum(["standard", "custom", "negotiated"]).optional(),
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
async (params) => {
|
|
435
|
+
const template = saveClauseTemplate(params);
|
|
436
|
+
return { content: [{ type: "text", text: JSON.stringify(template, null, 2) }] };
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
server.registerTool(
|
|
441
|
+
"list_clause_templates",
|
|
442
|
+
{
|
|
443
|
+
title: "List Clause Templates",
|
|
444
|
+
description: "List all clause templates in the clause library.",
|
|
445
|
+
inputSchema: {},
|
|
446
|
+
},
|
|
447
|
+
async () => {
|
|
448
|
+
const templates = listClauseTemplates();
|
|
449
|
+
return {
|
|
450
|
+
content: [
|
|
451
|
+
{ type: "text", text: JSON.stringify({ templates, count: templates.length }, null, 2) },
|
|
452
|
+
],
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
server.registerTool(
|
|
458
|
+
"add_clause_from_template",
|
|
459
|
+
{
|
|
460
|
+
title: "Add Clause from Template",
|
|
461
|
+
description: "Add a clause to a contract using a clause template by name.",
|
|
462
|
+
inputSchema: {
|
|
463
|
+
contract_id: z.string(),
|
|
464
|
+
template_name: z.string(),
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
async ({ contract_id, template_name }) => {
|
|
468
|
+
try {
|
|
469
|
+
const clause = addClauseFromTemplate(contract_id, template_name);
|
|
470
|
+
return { content: [{ type: "text", text: JSON.stringify(clause, null, 2) }] };
|
|
471
|
+
} catch (err) {
|
|
472
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
// --- Obligations ---
|
|
478
|
+
|
|
479
|
+
server.registerTool(
|
|
480
|
+
"add_obligation",
|
|
481
|
+
{
|
|
482
|
+
title: "Add Obligation",
|
|
483
|
+
description: "Add an obligation to a clause for tracking.",
|
|
484
|
+
inputSchema: {
|
|
485
|
+
clause_id: z.string(),
|
|
486
|
+
description: z.string(),
|
|
487
|
+
due_date: z.string().optional(),
|
|
488
|
+
assigned_to: z.string().optional(),
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
async (params) => {
|
|
492
|
+
const obligation = createObligation(params);
|
|
493
|
+
return { content: [{ type: "text", text: JSON.stringify(obligation, null, 2) }] };
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
server.registerTool(
|
|
498
|
+
"list_obligations",
|
|
499
|
+
{
|
|
500
|
+
title: "List Obligations",
|
|
501
|
+
description: "List all obligations for a clause.",
|
|
502
|
+
inputSchema: { clause_id: z.string() },
|
|
503
|
+
},
|
|
504
|
+
async ({ clause_id }) => {
|
|
505
|
+
const obligations = listObligations(clause_id);
|
|
506
|
+
return {
|
|
507
|
+
content: [
|
|
508
|
+
{ type: "text", text: JSON.stringify({ obligations, count: obligations.length }, null, 2) },
|
|
509
|
+
],
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
server.registerTool(
|
|
515
|
+
"complete_obligation",
|
|
516
|
+
{
|
|
517
|
+
title: "Complete Obligation",
|
|
518
|
+
description: "Mark an obligation as completed.",
|
|
519
|
+
inputSchema: { id: z.string() },
|
|
520
|
+
},
|
|
521
|
+
async ({ id }) => {
|
|
522
|
+
const obligation = completeObligation(id);
|
|
523
|
+
if (!obligation) {
|
|
524
|
+
return { content: [{ type: "text", text: `Obligation '${id}' not found.` }], isError: true };
|
|
525
|
+
}
|
|
526
|
+
return { content: [{ type: "text", text: JSON.stringify(obligation, null, 2) }] };
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
server.registerTool(
|
|
531
|
+
"list_overdue_obligations",
|
|
532
|
+
{
|
|
533
|
+
title: "List Overdue Obligations",
|
|
534
|
+
description: "List all overdue obligations across all contracts.",
|
|
535
|
+
inputSchema: {},
|
|
536
|
+
},
|
|
537
|
+
async () => {
|
|
538
|
+
const obligations = listOverdueObligations();
|
|
539
|
+
return {
|
|
540
|
+
content: [
|
|
541
|
+
{ type: "text", text: JSON.stringify({ obligations, count: obligations.length }, null, 2) },
|
|
542
|
+
],
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
|
|
262
547
|
// --- Reminders ---
|
|
263
548
|
|
|
264
549
|
server.registerTool(
|
|
@@ -295,6 +580,30 @@ server.registerTool(
|
|
|
295
580
|
}
|
|
296
581
|
);
|
|
297
582
|
|
|
583
|
+
server.registerTool(
|
|
584
|
+
"set_multi_reminders",
|
|
585
|
+
{
|
|
586
|
+
title: "Set Multi-Stage Reminders",
|
|
587
|
+
description: "Set multiple reminders at once based on days before contract end date.",
|
|
588
|
+
inputSchema: {
|
|
589
|
+
contract_id: z.string(),
|
|
590
|
+
days_before: z.array(z.number()),
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
async ({ contract_id, days_before }) => {
|
|
594
|
+
try {
|
|
595
|
+
const reminders = setMultiReminders(contract_id, days_before);
|
|
596
|
+
return {
|
|
597
|
+
content: [
|
|
598
|
+
{ type: "text", text: JSON.stringify({ reminders, count: reminders.length }, null, 2) },
|
|
599
|
+
],
|
|
600
|
+
};
|
|
601
|
+
} catch (err) {
|
|
602
|
+
return { content: [{ type: "text", text: (err as Error).message }], isError: true };
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
);
|
|
606
|
+
|
|
298
607
|
// --- Start ---
|
|
299
608
|
async function main() {
|
|
300
609
|
const transport = new StdioServerTransport();
|
|
@@ -19,7 +19,18 @@ import {
|
|
|
19
19
|
createAlert,
|
|
20
20
|
listAlerts,
|
|
21
21
|
deleteAlert,
|
|
22
|
+
whoisLookup,
|
|
23
|
+
checkDnsPropagation,
|
|
24
|
+
checkSsl,
|
|
25
|
+
exportZoneFile,
|
|
26
|
+
importZoneFile,
|
|
27
|
+
discoverSubdomains,
|
|
28
|
+
validateDns,
|
|
29
|
+
exportPortfolio,
|
|
30
|
+
checkAllDomains,
|
|
31
|
+
getDomainByName,
|
|
22
32
|
} from "../db/domains.js";
|
|
33
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
23
34
|
|
|
24
35
|
const program = new Command();
|
|
25
36
|
|
|
@@ -269,6 +280,109 @@ program
|
|
|
269
280
|
}
|
|
270
281
|
});
|
|
271
282
|
|
|
283
|
+
// --- WHOIS Lookup ---
|
|
284
|
+
|
|
285
|
+
program
|
|
286
|
+
.command("whois")
|
|
287
|
+
.description("Run WHOIS lookup for a domain and update DB record")
|
|
288
|
+
.argument("<name>", "Domain name (e.g. example.com)")
|
|
289
|
+
.option("--json", "Output as JSON", false)
|
|
290
|
+
.action((name, opts) => {
|
|
291
|
+
try {
|
|
292
|
+
const result = whoisLookup(name);
|
|
293
|
+
if (opts.json) {
|
|
294
|
+
console.log(JSON.stringify(result, null, 2));
|
|
295
|
+
} else {
|
|
296
|
+
console.log(`WHOIS for ${result.domain}:`);
|
|
297
|
+
console.log(` Registrar: ${result.registrar || "unknown"}`);
|
|
298
|
+
console.log(` Expires: ${result.expires_at || "unknown"}`);
|
|
299
|
+
if (result.nameservers.length > 0) {
|
|
300
|
+
console.log(` Nameservers: ${result.nameservers.join(", ")}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} catch (error: unknown) {
|
|
304
|
+
console.error(`WHOIS lookup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// --- SSL Check ---
|
|
310
|
+
|
|
311
|
+
program
|
|
312
|
+
.command("ssl-check")
|
|
313
|
+
.description("Check SSL certificate for a domain and update DB record")
|
|
314
|
+
.argument("<name>", "Domain name (e.g. example.com)")
|
|
315
|
+
.option("--json", "Output as JSON", false)
|
|
316
|
+
.action((name, opts) => {
|
|
317
|
+
const result = checkSsl(name);
|
|
318
|
+
if (opts.json) {
|
|
319
|
+
console.log(JSON.stringify(result, null, 2));
|
|
320
|
+
} else {
|
|
321
|
+
if (result.error) {
|
|
322
|
+
console.error(`SSL check failed: ${result.error}`);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
console.log(`SSL Certificate for ${result.domain}:`);
|
|
326
|
+
console.log(` Issuer: ${result.issuer || "unknown"}`);
|
|
327
|
+
console.log(` Expires: ${result.expires_at || "unknown"}`);
|
|
328
|
+
if (result.subject) console.log(` Subject: ${result.subject}`);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// --- Portfolio Export ---
|
|
333
|
+
|
|
334
|
+
program
|
|
335
|
+
.command("export")
|
|
336
|
+
.description("Export all domains as CSV or JSON")
|
|
337
|
+
.option("--format <format>", "Export format (csv or json)", "json")
|
|
338
|
+
.option("--output <file>", "Write to file instead of stdout")
|
|
339
|
+
.action((opts) => {
|
|
340
|
+
const format = opts.format === "csv" ? "csv" : "json";
|
|
341
|
+
const output = exportPortfolio(format as "csv" | "json");
|
|
342
|
+
if (opts.output) {
|
|
343
|
+
writeFileSync(opts.output, output, "utf-8");
|
|
344
|
+
console.log(`Exported to ${opts.output}`);
|
|
345
|
+
} else {
|
|
346
|
+
console.log(output);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// --- Bulk Domain Check ---
|
|
351
|
+
|
|
352
|
+
program
|
|
353
|
+
.command("check-all")
|
|
354
|
+
.description("Run WHOIS + SSL + DNS validation on all domains")
|
|
355
|
+
.option("--json", "Output as JSON", false)
|
|
356
|
+
.action((opts) => {
|
|
357
|
+
const results = checkAllDomains();
|
|
358
|
+
if (opts.json) {
|
|
359
|
+
console.log(JSON.stringify(results, null, 2));
|
|
360
|
+
} else {
|
|
361
|
+
if (results.length === 0) {
|
|
362
|
+
console.log("No domains to check.");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
for (const r of results) {
|
|
366
|
+
console.log(`\n${r.domain}:`);
|
|
367
|
+
if (r.whois) {
|
|
368
|
+
console.log(` WHOIS: registrar=${r.whois.registrar || "?"}, expires=${r.whois.expires_at || "?"}`);
|
|
369
|
+
if (r.whois.error) console.log(` Error: ${r.whois.error}`);
|
|
370
|
+
}
|
|
371
|
+
if (r.ssl) {
|
|
372
|
+
console.log(` SSL: issuer=${r.ssl.issuer || "?"}, expires=${r.ssl.expires_at || "?"}`);
|
|
373
|
+
if (r.ssl.error) console.log(` Error: ${r.ssl.error}`);
|
|
374
|
+
}
|
|
375
|
+
if (r.dns_validation) {
|
|
376
|
+
console.log(` DNS: valid=${r.dns_validation.valid}, issues=${r.dns_validation.issue_count}`);
|
|
377
|
+
for (const e of r.dns_validation.errors) {
|
|
378
|
+
console.log(` ${e}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
console.log(`\nChecked ${results.length} domain(s)`);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
272
386
|
// --- DNS Records ---
|
|
273
387
|
|
|
274
388
|
const dnsCmd = program
|
|
@@ -370,6 +484,145 @@ dnsCmd
|
|
|
370
484
|
}
|
|
371
485
|
});
|
|
372
486
|
|
|
487
|
+
// --- DNS Propagation Check ---
|
|
488
|
+
|
|
489
|
+
dnsCmd
|
|
490
|
+
.command("check-propagation")
|
|
491
|
+
.description("Check DNS propagation across multiple servers")
|
|
492
|
+
.argument("<domain>", "Domain name to check")
|
|
493
|
+
.option("--record <type>", "Record type (A/AAAA/CNAME/MX/TXT/NS)", "A")
|
|
494
|
+
.option("--json", "Output as JSON", false)
|
|
495
|
+
.action((domain, opts) => {
|
|
496
|
+
const result = checkDnsPropagation(domain, opts.record);
|
|
497
|
+
if (opts.json) {
|
|
498
|
+
console.log(JSON.stringify(result, null, 2));
|
|
499
|
+
} else {
|
|
500
|
+
console.log(`DNS Propagation for ${result.domain} (${result.record_type}):`);
|
|
501
|
+
console.log(` Consistent: ${result.consistent ? "yes" : "NO"}`);
|
|
502
|
+
for (const s of result.servers) {
|
|
503
|
+
const values = s.values.length > 0 ? s.values.join(", ") : "(empty)";
|
|
504
|
+
const status = s.status === "error" ? ` [ERROR: ${s.error}]` : "";
|
|
505
|
+
console.log(` ${s.name} (${s.server}): ${values}${status}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
// --- Zone File Export ---
|
|
511
|
+
|
|
512
|
+
dnsCmd
|
|
513
|
+
.command("export")
|
|
514
|
+
.description("Export DNS records as BIND zone file")
|
|
515
|
+
.argument("<domain-id>", "Domain ID")
|
|
516
|
+
.option("--format <format>", "Export format (zone)", "zone")
|
|
517
|
+
.option("--output <file>", "Write to file instead of stdout")
|
|
518
|
+
.action((domainId, opts) => {
|
|
519
|
+
const zone = exportZoneFile(domainId);
|
|
520
|
+
if (!zone) {
|
|
521
|
+
console.error(`Domain '${domainId}' not found.`);
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
if (opts.output) {
|
|
525
|
+
writeFileSync(opts.output, zone, "utf-8");
|
|
526
|
+
console.log(`Exported zone file to ${opts.output}`);
|
|
527
|
+
} else {
|
|
528
|
+
console.log(zone);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// --- Zone File Import ---
|
|
533
|
+
|
|
534
|
+
dnsCmd
|
|
535
|
+
.command("import")
|
|
536
|
+
.description("Import DNS records from a BIND zone file")
|
|
537
|
+
.argument("<domain-id>", "Domain ID")
|
|
538
|
+
.requiredOption("--file <path>", "Path to zone file")
|
|
539
|
+
.option("--json", "Output as JSON", false)
|
|
540
|
+
.action((domainId, opts) => {
|
|
541
|
+
let content: string;
|
|
542
|
+
try {
|
|
543
|
+
content = readFileSync(opts.file, "utf-8");
|
|
544
|
+
} catch {
|
|
545
|
+
console.error(`Could not read file: ${opts.file}`);
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const result = importZoneFile(domainId, content);
|
|
550
|
+
if (!result) {
|
|
551
|
+
console.error(`Domain '${domainId}' not found.`);
|
|
552
|
+
process.exit(1);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (opts.json) {
|
|
556
|
+
console.log(JSON.stringify(result, null, 2));
|
|
557
|
+
} else {
|
|
558
|
+
console.log(`Imported ${result.imported} record(s), skipped ${result.skipped}`);
|
|
559
|
+
if (result.errors.length > 0) {
|
|
560
|
+
console.log("Errors:");
|
|
561
|
+
for (const e of result.errors) {
|
|
562
|
+
console.log(` - ${e}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// --- Subdomain Discovery ---
|
|
569
|
+
|
|
570
|
+
dnsCmd
|
|
571
|
+
.command("discover-subdomains")
|
|
572
|
+
.description("Discover subdomains via certificate transparency logs (crt.sh)")
|
|
573
|
+
.argument("<domain>", "Domain name")
|
|
574
|
+
.option("--json", "Output as JSON", false)
|
|
575
|
+
.action(async (domain, opts) => {
|
|
576
|
+
const result = await discoverSubdomains(domain);
|
|
577
|
+
if (opts.json) {
|
|
578
|
+
console.log(JSON.stringify(result, null, 2));
|
|
579
|
+
} else {
|
|
580
|
+
if (result.error) {
|
|
581
|
+
console.error(`Discovery failed: ${result.error}`);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
if (result.subdomains.length === 0) {
|
|
585
|
+
console.log(`No subdomains found for ${domain}.`);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
console.log(`Subdomains for ${domain} (source: ${result.source}):`);
|
|
589
|
+
for (const s of result.subdomains) {
|
|
590
|
+
console.log(` ${s}`);
|
|
591
|
+
}
|
|
592
|
+
console.log(`\n${result.subdomains.length} subdomain(s) found`);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// --- DNS Validation ---
|
|
597
|
+
|
|
598
|
+
dnsCmd
|
|
599
|
+
.command("validate")
|
|
600
|
+
.description("Validate DNS records for common issues")
|
|
601
|
+
.argument("<domain-id>", "Domain ID")
|
|
602
|
+
.option("--json", "Output as JSON", false)
|
|
603
|
+
.action((domainId, opts) => {
|
|
604
|
+
const result = validateDns(domainId);
|
|
605
|
+
if (!result) {
|
|
606
|
+
console.error(`Domain '${domainId}' not found.`);
|
|
607
|
+
process.exit(1);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (opts.json) {
|
|
611
|
+
console.log(JSON.stringify(result, null, 2));
|
|
612
|
+
} else {
|
|
613
|
+
console.log(`DNS Validation for ${result.domain_name}:`);
|
|
614
|
+
console.log(` Valid: ${result.valid ? "yes" : "NO"}`);
|
|
615
|
+
if (result.issues.length === 0) {
|
|
616
|
+
console.log(" No issues found.");
|
|
617
|
+
} else {
|
|
618
|
+
for (const issue of result.issues) {
|
|
619
|
+
const prefix = issue.type === "error" ? "ERROR" : "WARN";
|
|
620
|
+
console.log(` [${prefix}] ${issue.message}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
373
626
|
// --- Alerts ---
|
|
374
627
|
|
|
375
628
|
const alertCmd = program
|