@hasna/microservices 0.0.8 → 0.0.10
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/bin/index.js +150 -35
- package/bin/mcp.js +67 -3
- package/dist/index.js +34 -2
- package/microservices/microservice-leads/package.json +27 -0
- package/microservices/microservice-leads/src/cli/index.ts +596 -0
- package/microservices/microservice-leads/src/db/database.ts +93 -0
- package/microservices/microservice-leads/src/db/leads.ts +520 -0
- package/microservices/microservice-leads/src/db/lists.ts +151 -0
- package/microservices/microservice-leads/src/db/migrations.ts +93 -0
- package/microservices/microservice-leads/src/index.ts +65 -0
- package/microservices/microservice-leads/src/lib/enrichment.ts +202 -0
- package/microservices/microservice-leads/src/lib/scoring.ts +134 -0
- package/microservices/microservice-leads/src/mcp/index.ts +533 -0
- package/microservices/microservice-transcriber/src/cli/index.ts +247 -1
- package/microservices/microservice-transcriber/src/db/comments.ts +166 -0
- package/microservices/microservice-transcriber/src/db/migrations.ts +46 -0
- package/microservices/microservice-transcriber/src/db/proofread.ts +119 -0
- package/microservices/microservice-transcriber/src/lib/downloader.ts +68 -0
- package/microservices/microservice-transcriber/src/lib/proofread.ts +296 -0
- package/microservices/microservice-transcriber/src/mcp/index.ts +263 -3
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -3519,8 +3519,8 @@ var {
|
|
|
3519
3519
|
|
|
3520
3520
|
// src/cli/index.tsx
|
|
3521
3521
|
import chalk2 from "chalk";
|
|
3522
|
-
import { existsSync as
|
|
3523
|
-
import { join as
|
|
3522
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3523
|
+
import { join as join5 } from "path";
|
|
3524
3524
|
import { homedir } from "os";
|
|
3525
3525
|
|
|
3526
3526
|
// src/cli/components/App.tsx
|
|
@@ -4028,6 +4028,13 @@ var MICROSERVICES = [
|
|
|
4028
4028
|
category: "CRM",
|
|
4029
4029
|
tags: ["social-media", "posts", "scheduling", "engagement", "analytics", "x", "linkedin", "instagram"]
|
|
4030
4030
|
},
|
|
4031
|
+
{
|
|
4032
|
+
name: "leads",
|
|
4033
|
+
displayName: "Leads",
|
|
4034
|
+
description: "Lead generation, storage, scoring, and data enrichment with pipeline tracking, bulk import/export, and deduplication",
|
|
4035
|
+
category: "CRM",
|
|
4036
|
+
tags: ["leads", "lead-generation", "scoring", "enrichment", "pipeline", "dedup", "import", "export"]
|
|
4037
|
+
},
|
|
4031
4038
|
{
|
|
4032
4039
|
name: "inventory",
|
|
4033
4040
|
displayName: "Inventory",
|
|
@@ -4862,6 +4869,28 @@ function resolveSourceDir() {
|
|
|
4862
4869
|
return fromBin;
|
|
4863
4870
|
}
|
|
4864
4871
|
var SOURCE_DIR = resolveSourceDir();
|
|
4872
|
+
var PRESERVED_DB_FILES = new Set(["data.db", "data.db-wal", "data.db-shm"]);
|
|
4873
|
+
function getCliCandidates(baseDir) {
|
|
4874
|
+
return [
|
|
4875
|
+
join2(baseDir, "src", "cli", "index.ts"),
|
|
4876
|
+
join2(baseDir, "cli.ts"),
|
|
4877
|
+
join2(baseDir, "src", "index.ts")
|
|
4878
|
+
];
|
|
4879
|
+
}
|
|
4880
|
+
function hasInstalledSource(baseDir) {
|
|
4881
|
+
return getCliCandidates(baseDir).some((candidate) => existsSync2(candidate));
|
|
4882
|
+
}
|
|
4883
|
+
function clearInstalledSource(baseDir) {
|
|
4884
|
+
if (!existsSync2(baseDir)) {
|
|
4885
|
+
return;
|
|
4886
|
+
}
|
|
4887
|
+
for (const entry of readdirSync(baseDir)) {
|
|
4888
|
+
if (PRESERVED_DB_FILES.has(entry)) {
|
|
4889
|
+
continue;
|
|
4890
|
+
}
|
|
4891
|
+
rmSync(join2(baseDir, entry), { recursive: true, force: true });
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4865
4894
|
function getMicroservicePath(name) {
|
|
4866
4895
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
4867
4896
|
return join2(SOURCE_DIR, msName);
|
|
@@ -4899,6 +4928,9 @@ function installMicroservice(name, options = {}) {
|
|
|
4899
4928
|
if (!existsSync2(targetDir)) {
|
|
4900
4929
|
mkdirSync2(targetDir, { recursive: true });
|
|
4901
4930
|
}
|
|
4931
|
+
if (existsSync2(destPath) && overwrite) {
|
|
4932
|
+
clearInstalledSource(destPath);
|
|
4933
|
+
}
|
|
4902
4934
|
cpSync(sourcePath, destPath, { recursive: true });
|
|
4903
4935
|
return {
|
|
4904
4936
|
microservice: cleanName,
|
|
@@ -4923,7 +4955,7 @@ function getInstalledMicroservices(targetDir) {
|
|
|
4923
4955
|
}
|
|
4924
4956
|
return readdirSync(dir).filter((f) => {
|
|
4925
4957
|
const fullPath = join2(dir, f);
|
|
4926
|
-
return f.startsWith("microservice-") && statSync(fullPath).isDirectory();
|
|
4958
|
+
return f.startsWith("microservice-") && statSync(fullPath).isDirectory() && hasInstalledSource(fullPath);
|
|
4927
4959
|
}).map((f) => f.replace("microservice-", ""));
|
|
4928
4960
|
}
|
|
4929
4961
|
function removeMicroservice(name, options = {}) {
|
|
@@ -4952,7 +4984,7 @@ function getMicroserviceStatus(name) {
|
|
|
4952
4984
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
4953
4985
|
const msPath = join2(dir, msName);
|
|
4954
4986
|
const dbPath = join2(msPath, "data.db");
|
|
4955
|
-
const installed =
|
|
4987
|
+
const installed = hasInstalledSource(msPath);
|
|
4956
4988
|
const hasDatabase = existsSync2(dbPath);
|
|
4957
4989
|
let dbSizeBytes = 0;
|
|
4958
4990
|
if (hasDatabase) {
|
|
@@ -5274,6 +5306,65 @@ function App({ initialServices, overwrite = false }) {
|
|
|
5274
5306
|
}, undefined, true, undefined, this);
|
|
5275
5307
|
}
|
|
5276
5308
|
|
|
5309
|
+
// src/cli/config-utils.ts
|
|
5310
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
5311
|
+
import { dirname as dirname3 } from "path";
|
|
5312
|
+
function parseTimeoutMs(raw, fallback = 30000) {
|
|
5313
|
+
if (raw === undefined || raw.trim() === "") {
|
|
5314
|
+
return fallback;
|
|
5315
|
+
}
|
|
5316
|
+
const value = Number(raw);
|
|
5317
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
5318
|
+
throw new Error("Timeout must be a positive integer in milliseconds.");
|
|
5319
|
+
}
|
|
5320
|
+
return value;
|
|
5321
|
+
}
|
|
5322
|
+
function ensureParentDirectory(filePath) {
|
|
5323
|
+
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
5324
|
+
}
|
|
5325
|
+
function upsertClaudeMcpConfig(content, command) {
|
|
5326
|
+
const parsed = content ? JSON.parse(content) : {};
|
|
5327
|
+
const config = parsed;
|
|
5328
|
+
if (!config.mcpServers) {
|
|
5329
|
+
config.mcpServers = {};
|
|
5330
|
+
}
|
|
5331
|
+
config.mcpServers.microservices = {
|
|
5332
|
+
type: "stdio",
|
|
5333
|
+
command,
|
|
5334
|
+
args: [],
|
|
5335
|
+
env: {}
|
|
5336
|
+
};
|
|
5337
|
+
return JSON.stringify(config, null, 2);
|
|
5338
|
+
}
|
|
5339
|
+
function upsertCodexMcpConfig(content, command) {
|
|
5340
|
+
const existing = content ?? "";
|
|
5341
|
+
if (existing.includes("[mcp_servers.microservices]")) {
|
|
5342
|
+
return { content: existing, alreadyRegistered: true };
|
|
5343
|
+
}
|
|
5344
|
+
const trimmed = existing.trimEnd();
|
|
5345
|
+
const prefix = trimmed.length > 0 ? `${trimmed}
|
|
5346
|
+
|
|
5347
|
+
` : "";
|
|
5348
|
+
return {
|
|
5349
|
+
content: `${prefix}[mcp_servers.microservices]
|
|
5350
|
+
command = "${command}"
|
|
5351
|
+
`,
|
|
5352
|
+
alreadyRegistered: false
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
function upsertGeminiMcpConfig(content, command) {
|
|
5356
|
+
const parsed = content ? JSON.parse(content) : {};
|
|
5357
|
+
const config = parsed;
|
|
5358
|
+
if (!config.mcpServers) {
|
|
5359
|
+
config.mcpServers = {};
|
|
5360
|
+
}
|
|
5361
|
+
config.mcpServers.microservices = {
|
|
5362
|
+
command,
|
|
5363
|
+
args: []
|
|
5364
|
+
};
|
|
5365
|
+
return JSON.stringify(config, null, 2);
|
|
5366
|
+
}
|
|
5367
|
+
|
|
5277
5368
|
// src/lib/runner.ts
|
|
5278
5369
|
import { existsSync as existsSync3 } from "fs";
|
|
5279
5370
|
import { join as join3 } from "path";
|
|
@@ -5348,11 +5439,43 @@ async function getMicroserviceOperations(name) {
|
|
|
5348
5439
|
return { commands, helpText };
|
|
5349
5440
|
}
|
|
5350
5441
|
|
|
5442
|
+
// src/lib/package-info.ts
|
|
5443
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
5444
|
+
import { dirname as dirname4, join as join4, resolve as resolve2 } from "path";
|
|
5445
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5446
|
+
var DEFAULT_VERSION = "0.0.1";
|
|
5447
|
+
function findNearestPackageJson(startDir) {
|
|
5448
|
+
let dir = resolve2(startDir);
|
|
5449
|
+
while (true) {
|
|
5450
|
+
const candidate = join4(dir, "package.json");
|
|
5451
|
+
if (existsSync4(candidate)) {
|
|
5452
|
+
return candidate;
|
|
5453
|
+
}
|
|
5454
|
+
const parent = dirname4(dir);
|
|
5455
|
+
if (parent === dir) {
|
|
5456
|
+
return null;
|
|
5457
|
+
}
|
|
5458
|
+
dir = parent;
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
function readPackageVersion(packageJsonPath) {
|
|
5462
|
+
const raw = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
5463
|
+
return typeof raw.version === "string" && raw.version.length > 0 ? raw.version : DEFAULT_VERSION;
|
|
5464
|
+
}
|
|
5465
|
+
function getPackageVersion(startDir) {
|
|
5466
|
+
const resolvedStartDir = startDir ?? dirname4(fileURLToPath2(import.meta.url));
|
|
5467
|
+
const packageJsonPath = findNearestPackageJson(resolvedStartDir);
|
|
5468
|
+
if (!packageJsonPath) {
|
|
5469
|
+
return DEFAULT_VERSION;
|
|
5470
|
+
}
|
|
5471
|
+
return readPackageVersion(packageJsonPath);
|
|
5472
|
+
}
|
|
5473
|
+
|
|
5351
5474
|
// src/cli/index.tsx
|
|
5352
5475
|
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
5353
5476
|
var isTTY = process.stdout.isTTY ?? false;
|
|
5354
5477
|
var program2 = new Command;
|
|
5355
|
-
program2.name("microservices").description("Mini business apps for AI agents \u2014 invoices, contacts, bookkeeping and more").version(
|
|
5478
|
+
program2.name("microservices").description("Mini business apps for AI agents \u2014 invoices, contacts, bookkeeping and more").version(getPackageVersion()).enablePositionalOptions();
|
|
5356
5479
|
program2.command("interactive", { isDefault: true }).alias("i").description("Interactive microservice browser").action(() => {
|
|
5357
5480
|
if (!isTTY) {
|
|
5358
5481
|
console.log(`Non-interactive environment detected. Use a subcommand:
|
|
@@ -5540,7 +5663,14 @@ program2.command("run").argument("<name>", "Microservice name").argument("[args.
|
|
|
5540
5663
|
console.error(chalk2.red(`Microservice '${name}' is not installed or has no CLI.`));
|
|
5541
5664
|
process.exit(1);
|
|
5542
5665
|
}
|
|
5543
|
-
|
|
5666
|
+
let timeout;
|
|
5667
|
+
try {
|
|
5668
|
+
timeout = parseTimeoutMs(options.timeout);
|
|
5669
|
+
} catch (error) {
|
|
5670
|
+
console.error(chalk2.red(error instanceof Error ? error.message : String(error)));
|
|
5671
|
+
process.exit(1);
|
|
5672
|
+
}
|
|
5673
|
+
const result = await runMicroserviceCommand(name, args, timeout);
|
|
5544
5674
|
if (result.stdout)
|
|
5545
5675
|
console.log(result.stdout);
|
|
5546
5676
|
if (result.stderr)
|
|
@@ -5563,21 +5693,13 @@ program2.command("ops").argument("<name>", "Microservice name").description("Lis
|
|
|
5563
5693
|
program2.command("mcp").description("Register MCP server with AI coding agents").option("--register <target>", "Register with: claude, codex, gemini, or all", "all").action((options) => {
|
|
5564
5694
|
const target = options.register;
|
|
5565
5695
|
const mcpBin = "microservices-mcp";
|
|
5566
|
-
const mcpBinFull =
|
|
5696
|
+
const mcpBinFull = join5(homedir(), ".bun", "bin", "microservices-mcp");
|
|
5567
5697
|
let registered = 0;
|
|
5568
5698
|
if (target === "all" || target === "claude") {
|
|
5569
|
-
const claudePath =
|
|
5699
|
+
const claudePath = join5(homedir(), ".claude.json");
|
|
5570
5700
|
try {
|
|
5571
|
-
const
|
|
5572
|
-
|
|
5573
|
-
config.mcpServers = {};
|
|
5574
|
-
config.mcpServers.microservices = {
|
|
5575
|
-
type: "stdio",
|
|
5576
|
-
command: mcpBin,
|
|
5577
|
-
args: [],
|
|
5578
|
-
env: {}
|
|
5579
|
-
};
|
|
5580
|
-
writeFileSync2(claudePath, JSON.stringify(config, null, 2));
|
|
5701
|
+
const content = existsSync5(claudePath) ? readFileSync3(claudePath, "utf-8") : undefined;
|
|
5702
|
+
writeFileSync2(claudePath, upsertClaudeMcpConfig(content, mcpBin));
|
|
5581
5703
|
console.log(chalk2.green(" + Claude Code") + chalk2.gray(` (${claudePath})`));
|
|
5582
5704
|
registered++;
|
|
5583
5705
|
} catch (e) {
|
|
@@ -5585,17 +5707,15 @@ program2.command("mcp").description("Register MCP server with AI coding agents")
|
|
|
5585
5707
|
}
|
|
5586
5708
|
}
|
|
5587
5709
|
if (target === "all" || target === "codex") {
|
|
5588
|
-
const codexPath =
|
|
5710
|
+
const codexPath = join5(homedir(), ".codex", "config.toml");
|
|
5589
5711
|
try {
|
|
5590
|
-
|
|
5591
|
-
|
|
5712
|
+
const existing = existsSync5(codexPath) ? readFileSync3(codexPath, "utf-8") : undefined;
|
|
5713
|
+
const updated = upsertCodexMcpConfig(existing, mcpBin);
|
|
5714
|
+
if (updated.alreadyRegistered) {
|
|
5592
5715
|
console.log(chalk2.yellow(" ~ Codex CLI (already registered)"));
|
|
5593
5716
|
} else {
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
command = "${mcpBin}"
|
|
5597
|
-
`;
|
|
5598
|
-
writeFileSync2(codexPath, content);
|
|
5717
|
+
ensureParentDirectory(codexPath);
|
|
5718
|
+
writeFileSync2(codexPath, updated.content);
|
|
5599
5719
|
console.log(chalk2.green(" + Codex CLI") + chalk2.gray(` (${codexPath})`));
|
|
5600
5720
|
}
|
|
5601
5721
|
registered++;
|
|
@@ -5604,16 +5724,11 @@ command = "${mcpBin}"
|
|
|
5604
5724
|
}
|
|
5605
5725
|
}
|
|
5606
5726
|
if (target === "all" || target === "gemini") {
|
|
5607
|
-
const geminiPath =
|
|
5727
|
+
const geminiPath = join5(homedir(), ".gemini", "settings.json");
|
|
5608
5728
|
try {
|
|
5609
|
-
const
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
config.mcpServers.microservices = {
|
|
5613
|
-
command: mcpBinFull,
|
|
5614
|
-
args: []
|
|
5615
|
-
};
|
|
5616
|
-
writeFileSync2(geminiPath, JSON.stringify(config, null, 2));
|
|
5729
|
+
const content = existsSync5(geminiPath) ? readFileSync3(geminiPath, "utf-8") : undefined;
|
|
5730
|
+
ensureParentDirectory(geminiPath);
|
|
5731
|
+
writeFileSync2(geminiPath, upsertGeminiMcpConfig(content, mcpBinFull));
|
|
5617
5732
|
console.log(chalk2.green(" + Gemini CLI") + chalk2.gray(` (${geminiPath})`));
|
|
5618
5733
|
registered++;
|
|
5619
5734
|
} catch (e) {
|
package/bin/mcp.js
CHANGED
|
@@ -19529,6 +19529,13 @@ var MICROSERVICES = [
|
|
|
19529
19529
|
category: "CRM",
|
|
19530
19530
|
tags: ["social-media", "posts", "scheduling", "engagement", "analytics", "x", "linkedin", "instagram"]
|
|
19531
19531
|
},
|
|
19532
|
+
{
|
|
19533
|
+
name: "leads",
|
|
19534
|
+
displayName: "Leads",
|
|
19535
|
+
description: "Lead generation, storage, scoring, and data enrichment with pipeline tracking, bulk import/export, and deduplication",
|
|
19536
|
+
category: "CRM",
|
|
19537
|
+
tags: ["leads", "lead-generation", "scoring", "enrichment", "pipeline", "dedup", "import", "export"]
|
|
19538
|
+
},
|
|
19532
19539
|
{
|
|
19533
19540
|
name: "inventory",
|
|
19534
19541
|
displayName: "Inventory",
|
|
@@ -19677,6 +19684,28 @@ function resolveSourceDir() {
|
|
|
19677
19684
|
return fromBin;
|
|
19678
19685
|
}
|
|
19679
19686
|
var SOURCE_DIR = resolveSourceDir();
|
|
19687
|
+
var PRESERVED_DB_FILES = new Set(["data.db", "data.db-wal", "data.db-shm"]);
|
|
19688
|
+
function getCliCandidates(baseDir) {
|
|
19689
|
+
return [
|
|
19690
|
+
join2(baseDir, "src", "cli", "index.ts"),
|
|
19691
|
+
join2(baseDir, "cli.ts"),
|
|
19692
|
+
join2(baseDir, "src", "index.ts")
|
|
19693
|
+
];
|
|
19694
|
+
}
|
|
19695
|
+
function hasInstalledSource(baseDir) {
|
|
19696
|
+
return getCliCandidates(baseDir).some((candidate) => existsSync2(candidate));
|
|
19697
|
+
}
|
|
19698
|
+
function clearInstalledSource(baseDir) {
|
|
19699
|
+
if (!existsSync2(baseDir)) {
|
|
19700
|
+
return;
|
|
19701
|
+
}
|
|
19702
|
+
for (const entry of readdirSync(baseDir)) {
|
|
19703
|
+
if (PRESERVED_DB_FILES.has(entry)) {
|
|
19704
|
+
continue;
|
|
19705
|
+
}
|
|
19706
|
+
rmSync(join2(baseDir, entry), { recursive: true, force: true });
|
|
19707
|
+
}
|
|
19708
|
+
}
|
|
19680
19709
|
function getMicroservicePath(name) {
|
|
19681
19710
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
19682
19711
|
return join2(SOURCE_DIR, msName);
|
|
@@ -19714,6 +19743,9 @@ function installMicroservice(name, options = {}) {
|
|
|
19714
19743
|
if (!existsSync2(targetDir)) {
|
|
19715
19744
|
mkdirSync2(targetDir, { recursive: true });
|
|
19716
19745
|
}
|
|
19746
|
+
if (existsSync2(destPath) && overwrite) {
|
|
19747
|
+
clearInstalledSource(destPath);
|
|
19748
|
+
}
|
|
19717
19749
|
cpSync(sourcePath, destPath, { recursive: true });
|
|
19718
19750
|
return {
|
|
19719
19751
|
microservice: cleanName,
|
|
@@ -19735,7 +19767,7 @@ function getInstalledMicroservices(targetDir) {
|
|
|
19735
19767
|
}
|
|
19736
19768
|
return readdirSync(dir).filter((f) => {
|
|
19737
19769
|
const fullPath = join2(dir, f);
|
|
19738
|
-
return f.startsWith("microservice-") && statSync(fullPath).isDirectory();
|
|
19770
|
+
return f.startsWith("microservice-") && statSync(fullPath).isDirectory() && hasInstalledSource(fullPath);
|
|
19739
19771
|
}).map((f) => f.replace("microservice-", ""));
|
|
19740
19772
|
}
|
|
19741
19773
|
function removeMicroservice(name, options = {}) {
|
|
@@ -19764,7 +19796,7 @@ function getMicroserviceStatus(name) {
|
|
|
19764
19796
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
19765
19797
|
const msPath = join2(dir, msName);
|
|
19766
19798
|
const dbPath = join2(msPath, "data.db");
|
|
19767
|
-
const installed =
|
|
19799
|
+
const installed = hasInstalledSource(msPath);
|
|
19768
19800
|
const hasDatabase = existsSync2(dbPath);
|
|
19769
19801
|
let dbSizeBytes = 0;
|
|
19770
19802
|
if (hasDatabase) {
|
|
@@ -19855,10 +19887,42 @@ async function getMicroserviceOperations(name) {
|
|
|
19855
19887
|
return { commands, helpText };
|
|
19856
19888
|
}
|
|
19857
19889
|
|
|
19890
|
+
// src/lib/package-info.ts
|
|
19891
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
19892
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
19893
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19894
|
+
var DEFAULT_VERSION = "0.0.1";
|
|
19895
|
+
function findNearestPackageJson(startDir) {
|
|
19896
|
+
let dir = resolve2(startDir);
|
|
19897
|
+
while (true) {
|
|
19898
|
+
const candidate = join4(dir, "package.json");
|
|
19899
|
+
if (existsSync4(candidate)) {
|
|
19900
|
+
return candidate;
|
|
19901
|
+
}
|
|
19902
|
+
const parent = dirname3(dir);
|
|
19903
|
+
if (parent === dir) {
|
|
19904
|
+
return null;
|
|
19905
|
+
}
|
|
19906
|
+
dir = parent;
|
|
19907
|
+
}
|
|
19908
|
+
}
|
|
19909
|
+
function readPackageVersion(packageJsonPath) {
|
|
19910
|
+
const raw = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
19911
|
+
return typeof raw.version === "string" && raw.version.length > 0 ? raw.version : DEFAULT_VERSION;
|
|
19912
|
+
}
|
|
19913
|
+
function getPackageVersion(startDir) {
|
|
19914
|
+
const resolvedStartDir = startDir ?? dirname3(fileURLToPath2(import.meta.url));
|
|
19915
|
+
const packageJsonPath = findNearestPackageJson(resolvedStartDir);
|
|
19916
|
+
if (!packageJsonPath) {
|
|
19917
|
+
return DEFAULT_VERSION;
|
|
19918
|
+
}
|
|
19919
|
+
return readPackageVersion(packageJsonPath);
|
|
19920
|
+
}
|
|
19921
|
+
|
|
19858
19922
|
// src/mcp/index.ts
|
|
19859
19923
|
var server = new McpServer({
|
|
19860
19924
|
name: "microservices",
|
|
19861
|
-
version:
|
|
19925
|
+
version: getPackageVersion()
|
|
19862
19926
|
});
|
|
19863
19927
|
server.registerTool("search_microservices", {
|
|
19864
19928
|
title: "Search Microservices",
|
package/dist/index.js
CHANGED
|
@@ -73,6 +73,13 @@ var MICROSERVICES = [
|
|
|
73
73
|
category: "CRM",
|
|
74
74
|
tags: ["social-media", "posts", "scheduling", "engagement", "analytics", "x", "linkedin", "instagram"]
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: "leads",
|
|
78
|
+
displayName: "Leads",
|
|
79
|
+
description: "Lead generation, storage, scoring, and data enrichment with pipeline tracking, bulk import/export, and deduplication",
|
|
80
|
+
category: "CRM",
|
|
81
|
+
tags: ["leads", "lead-generation", "scoring", "enrichment", "pipeline", "dedup", "import", "export"]
|
|
82
|
+
},
|
|
76
83
|
{
|
|
77
84
|
name: "inventory",
|
|
78
85
|
displayName: "Inventory",
|
|
@@ -270,6 +277,28 @@ function resolveSourceDir() {
|
|
|
270
277
|
return fromBin;
|
|
271
278
|
}
|
|
272
279
|
var SOURCE_DIR = resolveSourceDir();
|
|
280
|
+
var PRESERVED_DB_FILES = new Set(["data.db", "data.db-wal", "data.db-shm"]);
|
|
281
|
+
function getCliCandidates(baseDir) {
|
|
282
|
+
return [
|
|
283
|
+
join2(baseDir, "src", "cli", "index.ts"),
|
|
284
|
+
join2(baseDir, "cli.ts"),
|
|
285
|
+
join2(baseDir, "src", "index.ts")
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
function hasInstalledSource(baseDir) {
|
|
289
|
+
return getCliCandidates(baseDir).some((candidate) => existsSync2(candidate));
|
|
290
|
+
}
|
|
291
|
+
function clearInstalledSource(baseDir) {
|
|
292
|
+
if (!existsSync2(baseDir)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
for (const entry of readdirSync(baseDir)) {
|
|
296
|
+
if (PRESERVED_DB_FILES.has(entry)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
rmSync(join2(baseDir, entry), { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
273
302
|
function getMicroservicePath(name) {
|
|
274
303
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
275
304
|
return join2(SOURCE_DIR, msName);
|
|
@@ -310,6 +339,9 @@ function installMicroservice(name, options = {}) {
|
|
|
310
339
|
if (!existsSync2(targetDir)) {
|
|
311
340
|
mkdirSync2(targetDir, { recursive: true });
|
|
312
341
|
}
|
|
342
|
+
if (existsSync2(destPath) && overwrite) {
|
|
343
|
+
clearInstalledSource(destPath);
|
|
344
|
+
}
|
|
313
345
|
cpSync(sourcePath, destPath, { recursive: true });
|
|
314
346
|
return {
|
|
315
347
|
microservice: cleanName,
|
|
@@ -334,7 +366,7 @@ function getInstalledMicroservices(targetDir) {
|
|
|
334
366
|
}
|
|
335
367
|
return readdirSync(dir).filter((f) => {
|
|
336
368
|
const fullPath = join2(dir, f);
|
|
337
|
-
return f.startsWith("microservice-") && statSync(fullPath).isDirectory();
|
|
369
|
+
return f.startsWith("microservice-") && statSync(fullPath).isDirectory() && hasInstalledSource(fullPath);
|
|
338
370
|
}).map((f) => f.replace("microservice-", ""));
|
|
339
371
|
}
|
|
340
372
|
function removeMicroservice(name, options = {}) {
|
|
@@ -363,7 +395,7 @@ function getMicroserviceStatus(name) {
|
|
|
363
395
|
const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
|
|
364
396
|
const msPath = join2(dir, msName);
|
|
365
397
|
const dbPath = join2(msPath, "data.db");
|
|
366
|
-
const installed =
|
|
398
|
+
const installed = hasInstalledSource(msPath);
|
|
367
399
|
const hasDatabase = existsSync2(dbPath);
|
|
368
400
|
let dbSizeBytes = 0;
|
|
369
401
|
if (hasDatabase) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasna/microservice-leads",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Lead generation, storage, scoring, and data enrichment microservice for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"microservice-leads": "./src/cli/index.ts",
|
|
8
|
+
"microservice-leads-mcp": "./src/mcp/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "bun run ./src/cli/index.ts",
|
|
15
|
+
"test": "bun test"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
19
|
+
"commander": "^12.1.0",
|
|
20
|
+
"zod": "^3.24.0"
|
|
21
|
+
},
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"registry": "https://registry.npmjs.org",
|
|
25
|
+
"access": "public"
|
|
26
|
+
}
|
|
27
|
+
}
|